Skip to content

Commit 8da12bc

Browse files
lewccBurzahDGamerL
authored
Makes playing cards playable -- significantly reworks card interactions (ParadiseSS13#26585)
* Basic card interactions * initial interactions sorted * nails down some interactions, radial menu * Some more qol and keybind changes * improves card interactions * Unum decks can be flipped, showing the top card * Decks can now be split and recombined * minor tweaks to multi-deck stuff * Clean up a bunch of interactions * more cleanups * more cleanups and documentation * remote attacking looks pretty good * minor cleanups * ci * parser errs * Remove some debug things, re-add signal after merge * Adds a whole bunch more qol. * Improves examine messages * ci * concealed cards will now show properly in radial * Fixes some weird proximity monitor and control issues * Address reviews, clean up examine a bit more * radial fixes * remove todos * Apply suggestions from code review Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com> Signed-off-by: Luc <89928798+lewcc@users.noreply.github.com> * better documentation, adds label of who did what * b etter grammar * augh * missing icon state * Update code/game/gamemodes/wizard/magic_tarot.dm Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com> Signed-off-by: Luc <89928798+lewcc@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com> Signed-off-by: Luc <89928798+lewcc@users.noreply.github.com> * Add sleep on proximity monitor setup to hopefully avoid mapload issues * been writing too much c lately * Try late initialize * late init * 🧌 * map load --------- Signed-off-by: Luc <89928798+lewcc@users.noreply.github.com> Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com> Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com>
1 parent cf31fa0 commit 8da12bc

File tree

11 files changed

+903
-177
lines changed

11 files changed

+903
-177
lines changed

code/__DEFINES/dcs/atom_signals.dm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,5 @@
143143
#define COMPONENT_NO_MOUSEDROP (1<<0)
144144
///from base of atom/MouseDrop_T: (/atom/from, /mob/user)
145145
#define COMSIG_MOUSEDROPPED_ONTO "mousedropped_onto"
146+
/// On a ranged attack: base of mob/living/carbon/human/RangedAttack (/mob/living/carbon/human)
147+
#define COMSIG_ATOM_RANGED_ATTACKED "atom_range_attacked"

code/__DEFINES/is_helpers.dm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@
7676

7777
#define isstack(I) (istype(I, /obj/item/stack))
7878

79+
#define istable(S) (istype(S, /obj/structure/table))
80+
7981
GLOBAL_LIST_INIT(pointed_types, typecacheof(list(
8082
/obj/item/pen,
8183
/obj/item/screwdriver,

code/__HELPERS/trait_helpers.dm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
237237
#define TRAIT_NPC_ZOMBIE "npc_zombie" // A trait for checking if a zombie should act like an NPC and attack
238238
#define TRAIT_ABSTRACT_HANDS "abstract_hands" // Mobs with this trait can only pick up abstract items.
239239
#define TRAIT_LANGUAGE_LOCKED "language_locked" // cant add/remove languages until removed (excludes babel because fuck everything i guess)
240+
#define TRAIT_PLAYING_CARDS "playing_cards"
240241
#define TRAIT_EMP_IMMUNE "emp_immune" //The mob will take no damage from EMPs
241242
#define TRAIT_EMP_RESIST "emp_resist" //The mob will take less damage from EMPs
242243
#define TRAIT_MINDFLAYER_NULLIFIED "flayer_nullified" //The mindflayer will not be able to activate their abilities, or drain swarms from people

code/_onclick/click.dm

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,11 @@
273273
/mob/proc/RangedAttack(atom/A, params)
274274
if(SEND_SIGNAL(src, COMSIG_MOB_ATTACK_RANGED, A, params) & COMPONENT_CANCEL_ATTACK_CHAIN)
275275
return TRUE
276+
if(SEND_SIGNAL(A, COMSIG_ATOM_RANGED_ATTACKED, src) & COMPONENT_CANCEL_ATTACK_CHAIN)
277+
return TRUE
278+
276279
/*
277280
Restrained ClickOn
278-
279281
Used when you are handcuffed and click things.
280282
Not currently used by anything but could easily be.
281283
*/

code/_onclick/other_mobs.dm

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141

4242
/mob/living/carbon/human/RangedAttack(atom/A, params)
4343
. = ..()
44+
if(.)
45+
return
4446
if(gloves)
4547
var/obj/item/clothing/gloves/G = gloves
4648
if(istype(G) && G.Touch(A, 0)) // for magic gloves
@@ -54,7 +56,6 @@
5456

5557
if(isturf(A) && get_dist(src, A) <= 1)
5658
Move_Pulled(A)
57-
5859
/*
5960
Animals & All Unspecified
6061
*/

code/datums/components/proximity_monitor.dm

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,3 +286,167 @@
286286
. = ..()
287287
if(active && AM != monitor.hasprox_receiver && !(AM in monitor.nested_receiver_locs))
288288
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)

code/game/gamemodes/wizard/magic_tarot.dm

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,25 +234,43 @@
234234
/obj/item/magic_tarot_card/proc/pre_activate(mob/user, atom/movable/thrower)
235235
has_been_activated = TRUE
236236
forceMove(user)
237-
var/obj/effect/temp_visual/tarot_preview/draft = new /obj/effect/temp_visual/tarot_preview(user, our_tarot.card_icon)
237+
var/obj/effect/temp_visual/card_preview/tarot/draft = new(user, "tarot_[our_tarot.card_icon]")
238238
user.vis_contents += draft
239239
user.visible_message("<span class='hierophant'>[user] holds up [src]!</span>")
240240
addtimer(CALLBACK(our_tarot, TYPE_PROC_REF(/datum/tarot, activate), user), 0.5 SECONDS)
241241
if(ismob(thrower) && our_tarot)
242242
add_attack_logs(thrower, user, "[thrower] has activated [our_tarot.name] on [user]", ATKLOG_FEW)
243243
QDEL_IN(src, 0.6 SECONDS)
244244

245-
/obj/effect/temp_visual/tarot_preview
246-
name = "a tarot card"
245+
/obj/effect/temp_visual/card_preview
246+
name = "a card"
247247
icon = 'icons/obj/playing_cards.dmi'
248248
icon_state = "tarot_the_unknown"
249249
pixel_y = 20
250250
duration = 1.5 SECONDS
251251

252-
/obj/effect/temp_visual/tarot_preview/Initialize(mapload, new_icon_state)
252+
/obj/effect/temp_visual/card_preview/Initialize(mapload, new_icon_state)
253253
. = ..()
254254
if(new_icon_state)
255-
icon_state = "tarot_[new_icon_state]"
255+
icon_state = new_icon_state
256+
257+
flourish()
258+
259+
/obj/effect/temp_visual/card_preview/proc/flourish()
260+
var/new_filter = isnull(get_filter("ray"))
261+
ray_filter_helper(1, 40, "#fcf3dc", 6, 20)
262+
if(new_filter)
263+
animate(get_filter("ray"), alpha = 0, offset = 10, time = duration, loop = -1)
264+
animate(offset = 0, time = duration)
265+
266+
/obj/effect/temp_visual/card_preview/tarot
267+
name = "a tarot card"
268+
icon = 'icons/obj/playing_cards.dmi'
269+
icon_state = "tarot_the_unknown"
270+
pixel_y = 20
271+
duration = 1.5 SECONDS
272+
273+
/obj/effect/temp_visual/card_preview/tarot/flourish()
256274
var/new_filter = isnull(get_filter("ray"))
257275
ray_filter_helper(1, 40,"#fcf3dc", 6, 20)
258276
if(new_filter)

0 commit comments

Comments
 (0)