Files
Bubberstation/code/game/objects/items/tcg/tcg_machines.dm
T
John WillardandGitHub b5975bd915 Unit tests now catch decals in walls/space (#75189)
## About The Pull Request

Unit tests will now fail if there's a decal in a wall or open space
turf. Open space turf could be limiting to mappers but I don't think it
makes sense for decals (like dirt, glass shards, etc) to be floating
around in space in the exact same spot.

If there's a decal you want to put in space, decals have a
``turf_loc_check`` var that will bypass this.

**Important note: This is not changing existing behavior. Decals already
delete themselves when they spawn in these incorrect locations, we're
just avoiding them from spawning in the first place.**

### Changes I made

- Ash flora are now lava immune, rivers spawn after flora does, so I
decided that it would be easiest (and more flavorful) to have them be
lava-immune rather than to not have them spawn at all.
- Decals can now be spawned in non-turf locations. This is currently
done by mail, which can give you bones as part of the mail. Currently it
will just delete itself instead.
- Trading Card button is now on the same tile as their display, which
now uses an offset. Before it would spawn it on the tile next to it,
which could be a wall in some instances.
- Mirrors now have floating movement type. They ARE floating since
they're attached to the wall, and it prevents them from burning up due
to lava in the Pride ruin.
- I also added a broken mirror subtype because I thought the icon_state
check was terrible.
- Bubblegum called ``DestroySurroundings`` several times on the same
thing, I hopefully fixed some of that. Their charge ability also
registered ``COMSIG_MOB_STATCHANGE`` despite ``/datum/action`` doing it
by default, so I fixed that too.

## Why It's Good For The Game

Decals in walls is already a bad idea, but currently all it does is
delete it on Initialize. It would be better if we ensured they wouldn't
spawn in the first place.

## Changelog

🆑
fix: Lava will no longer burn 6 of the mirrors in pride ruin
fix: Lava will no longer burn plants that spawn in them.
/🆑
2023-06-15 16:39:22 -07:00

468 lines
17 KiB
Plaintext

#define STAT_Y -23
#define POWER_X -14
#define RESOLVE_X 12
#define DEFAULT_POWER_COLOR "#af2323"
#define DEFAULT_RESOLVE_COLOR "#3492d0"
#define DEFAULT_MODIFIED_COLOR "#1db327"
/obj/machinery/trading_card_holder
name = "card slot"
desc = "A slot for placing Tactical Game Cards."
icon = 'icons/obj/toys/tcgmisc.dmi'
icon_state = "card_holder_inactive"
use_power = NO_POWER_USE
///Card thats currently inside the holder
var/obj/item/tcgcard/current_card
///Holds all the details such as stats for the card.
var/datum/card/card_template
///Reference to holographic currently active holographic summon
var/obj/structure/trading_card_summon/current_summon
///Color of the holograms produced.
var/team_color = "#77abff"
var/summon_offset_x = 0
var/summon_offset_y = 1
/obj/machinery/trading_card_holder/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/tcgcard) && current_summon == null)
current_card = I
card_template = current_card.extract_datum()
if(card_template.cardtype == "Creature")
if(!user.transferItemToLoc(current_card, src))
return
to_chat(user, span_notice("You put the [current_card] card in [src]."))
icon_state = "card_holder_active"
update_appearance()
current_summon = new(locate(x + summon_offset_x, y + summon_offset_y, z))
current_summon.template = card_template
current_summon.card_ref = current_card
current_summon.team_color = team_color
current_summon.load_model()
else
to_chat(user, span_notice("The [src] smartly rejects the non-creature card."))
current_card = null
return ..()
else
return ..()
GLOBAL_LIST_EMPTY(tcgcard_machine_radial_choices)
/obj/machinery/trading_card_holder/attack_hand(mob/user)
if(current_summon)
var/list/choices = GLOB.tcgcard_machine_radial_choices
if(!length(choices))
choices = GLOB.tcgcard_machine_radial_choices = list(
"Pickup" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_pickup"),
"Tap" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_tap"),
"Mark" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_mark"),
"Modify" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_modify"),
)
var/choice = show_radial_menu(user, src, choices, custom_check = CALLBACK(src, PROC_REF(check_menu), user), require_near = TRUE, tooltips = TRUE)
if(!check_menu(user))
return COMPONENT_CANCEL_ATTACK_CHAIN
switch(choice)
if("Tap")
current_summon.update_tapped()
if("Pickup")
if(current_card)
user.put_in_hands(current_card)
to_chat(user, span_notice("You take the [current_card] card out of [src]."))
current_card = null
else
to_chat(user, span_notice("The blank card dematerializes."))
card_template = null
icon_state = "card_holder_inactive"
update_appearance()
if(current_summon)
qdel(current_summon)
current_summon = null
if("Modify")
current_summon.modify_stats(user)
if("Mark")
current_summon.update_marked()
if(null)
return
else
to_chat(user, span_warning("[src] is empty!"))
add_fingerprint(user)
return ..()
/obj/machinery/trading_card_holder/attack_hand_secondary(mob/user)
if(isnull(current_summon))
var/card_name = tgui_input_text(user, "Insert card name", "Blank Card Naming", "blank card", MAX_NAME_LEN)
if(isnull(card_name))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
current_summon = new /obj/structure/trading_card_summon/blank(locate(x + summon_offset_x, y + summon_offset_y, z))
icon_state = "card_holder_active"
current_summon.name = card_name
current_summon.team_color = team_color
current_summon.load_model()
else
to_chat(user, span_notice("The [src] already contains a card."))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/machinery/trading_card_holder/proc/check_menu(mob/living/user)
if(!istype(user))
return FALSE
if(user.incapacitated() || !user.Adjacent(src))
return FALSE
return TRUE
/obj/machinery/trading_card_holder/Destroy()
if(current_summon)
qdel(current_summon)
. = ..()
/obj/machinery/trading_card_holder/examine(mob/user)
. = ..()
if(card_template)
. += span_notice("There is currently a [card_template.name] card inserted.")
else
if(current_summon)
. += span_notice("There is currently a blank card inserted.")
else
. += span_notice("There is no card currently inserted.")
/obj/machinery/trading_card_holder/red
summon_offset_y = -1
team_color = "#ff7777"
/obj/structure/trading_card_summon
name = "coder"
icon = 'icons/obj/toys/tcgsummons.dmi'
icon_state = "hologram"
anchored = TRUE
///Holds all the default details of the card.
var/datum/card/template
///Holds a reference to the card itself.
var/obj/item/tcgcard/card_ref
///Power statistics for the hologram, stored seperately to the template as they can be modified.
var/summon_power
///Resolve statistics for the hologram, stored seperately to the template as they can be modified.
var/summon_resolve
///Is the card tapped (rotated) or not.
var/tapped = FALSE
///Is the card marked or not.
var/marked = FALSE
///Reference to the hologram object itself.
var/obj/effect/overlay/card_summon/hologram
///Reference to the text overlay for power.
var/obj/effect/overlay/status_display_text/power_overlay
///Reference to the text overlay for resolve.
var/obj/effect/overlay/status_display_text/resolve_overlay
///Color of the power stat.
var/power_color = DEFAULT_POWER_COLOR
///Color of the resolve stat.
var/resolve_color = DEFAULT_RESOLVE_COLOR
///Color that stats become if they've been changed from their default.
var/modified_color = DEFAULT_MODIFIED_COLOR
///Color of the holograms produced.
var/team_color = "#77abff"
/obj/structure/trading_card_summon/proc/load_model()
hologram = new(loc)
hologram.icon = file(template.summon_icon_file)
hologram.icon_state = template.summon_icon_state
hologram.name = name
hologram.alpha = 170
hologram.add_atom_colour(team_color, FIXED_COLOUR_PRIORITY)
name = template.name
desc = template.desc
summon_power = template.power
summon_resolve = template.resolve
update_overlays()
/obj/structure/trading_card_summon/get_name_chaser(mob/user, list/name_chaser = list())
name_chaser += "Faction: [template.faction]"
name_chaser += "Cost: [template.summoncost]"
name_chaser += "Type: [template.cardtype] - [template.cardsubtype]"
name_chaser += "Power/Resolve: [summon_power]/[summon_resolve]"
if(template.rules) //This can sometimes be empty
name_chaser += "Ruleset: [template.rules]"
name_chaser += list("[icon2html(card_ref.get_cached_flat_icon(), user, "extra_classes" = "hugeicon")]")
return name_chaser
/obj/structure/trading_card_summon/update_overlays()
. = ..()
var/overlay = update_stats(power_overlay, STAT_Y, summon_power, power_color, x_offset = POWER_X)
if(overlay)
power_overlay = overlay
overlay = update_stats(resolve_overlay, STAT_Y, summon_resolve, resolve_color, x_offset = RESOLVE_X)
if(overlay)
resolve_overlay = overlay
if(marked)
var/mutable_appearance/mark_overlay = mutable_appearance('icons/obj/toys/tcgmisc.dmi', "gem_green", 9)
mark_overlay.pixel_w = 12
mark_overlay.pixel_z = 12
. += mark_overlay
/obj/structure/trading_card_summon/proc/update_stats(obj/effect/overlay/status_display_text/overlay, pos_y, stats, text_color, x_offset)
if(overlay && stats == overlay.message)
return null
if(overlay)
qdel(overlay)
var/obj/effect/overlay/status_display_text/stats_display = new(src, pos_y, stats, text_color, text_color, x_offset)
stats_display.pixel_y = -32
stats_display.pixel_z = 32
vis_contents += stats_display
return stats_display
/obj/structure/trading_card_summon/proc/update_tapped()
if(tapped)
hologram.transform = turn(hologram.transform, 90)
else
hologram.transform = turn(hologram.transform, -90)
tapped = !tapped
/obj/structure/trading_card_summon/proc/update_marked()
marked = !marked
update_icon(UPDATE_OVERLAYS)
/obj/structure/trading_card_summon/proc/modify_stats(mob/living/user)
summon_power = num2text(tgui_input_number(user, "Please input power value", "Stat Modification", text2num(template.power), 25))
if(summon_power == template.power)
power_color = DEFAULT_POWER_COLOR
else
power_color = modified_color
summon_resolve = num2text(tgui_input_number(user, "Please input resolve value", "Stat Modification", text2num(template.resolve), 25))
if(summon_resolve == template.resolve)
resolve_color = DEFAULT_RESOLVE_COLOR
else
resolve_color = modified_color
update_overlays()
/obj/structure/trading_card_summon/Destroy()
if(hologram)
qdel(hologram)
return ..()
/obj/structure/trading_card_summon/blank
name = "blank card"
desc = "A blank card used to represent cards summoned by other cards."
summon_power = 1
summon_resolve = 1
power_color = DEFAULT_MODIFIED_COLOR
resolve_color = DEFAULT_MODIFIED_COLOR
/obj/structure/trading_card_summon/blank/load_model()
hologram = new(loc)
hologram.icon = 'icons/obj/toys/tcgmisc.dmi'
hologram.icon_state = "cardback"
hologram.name = name
hologram.alpha = 170
hologram.add_atom_colour(team_color, FIXED_COLOUR_PRIORITY)
update_overlays()
/obj/structure/trading_card_summon/blank/get_name_chaser(mob/user, list/name_chaser)
name_chaser += "Power/Resolve: [summon_power]/[summon_resolve]"
return name_chaser
/obj/structure/trading_card_summon/blank/modify_stats(mob/living/user)
summon_power = num2text(tgui_input_number(user, "Please input power value", "Stat Modification", text2num(summon_power), 25))
summon_resolve = num2text(tgui_input_number(user, "Please input resolve value", "Stat Modification", text2num(summon_resolve), 25))
update_overlays()
#undef STAT_Y
#undef POWER_X
#undef RESOLVE_X
#undef DEFAULT_POWER_COLOR
#undef DEFAULT_RESOLVE_COLOR
#undef DEFAULT_MODIFIED_COLOR
/obj/effect/overlay/card_summon
mouse_opacity = 0
///A button that generates a player manipulable bar of icons, in this case a mana bar.
/obj/machinery/trading_card_button
name = "mana control panel"
desc = "A set of buttons that lets you keep track of your mana when playing Tactical Game Cards."
icon = 'icons/obj/toys/tcgmisc.dmi'
icon_state = "mana_buttons"
use_power = NO_POWER_USE
///Reference to the display panel generated by this button.
var/obj/effect/decal/trading_card_panel/display_panel_ref
///Typepath of the display panel generated.
var/display_panel_type = /obj/effect/decal/trading_card_panel
///Where the panel will be spawned in relation to the button on the X axis.
var/panel_offset_x = 1
///Where the panel will be spawned in relation to the button on the Y axis.
var/panel_offset_y = 0
///Global list containing all options used for the TGC mana button.
GLOBAL_LIST_EMPTY(tcgcard_mana_bar_radial_choices)
/obj/machinery/trading_card_button/Initialize(mapload)
. = ..()
var/obj/effect/decal/trading_card_panel/new_panel = new display_panel_type(get_turf(src))
new_panel.pixel_x = panel_offset_x
new_panel.pixel_y = panel_offset_y
display_panel_ref = new_panel
/obj/machinery/trading_card_button/Destroy()
QDEL_NULL(display_panel_ref)
return ..()
/obj/machinery/trading_card_button/attack_hand(mob/user)
var/list/choices = setup_global()
if(!length(choices))
choices = setup_radial()
var/choice = show_radial_menu(user, src, choices, custom_check = CALLBACK(src, PROC_REF(check_menu), user), require_near = TRUE, tooltips = TRUE)
if(!check_menu(user))
return COMPONENT_CANCEL_ATTACK_CHAIN
handle_choice(choice, user)
display_panel_ref.update_icon(UPDATE_OVERLAYS)
add_fingerprint(user)
return ..()
///Proc that determines which global list of radial choices is used.
/obj/machinery/trading_card_button/proc/setup_global()
return GLOB.tcgcard_mana_bar_radial_choices
/obj/machinery/trading_card_button/proc/setup_radial()
var/radial_choices
radial_choices = GLOB.tcgcard_mana_bar_radial_choices = list(
"Set Mana" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_set_mana"),
"Set Mana Slots" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_set_slots"),
"Next Turn" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_next"),
)
return radial_choices
/obj/machinery/trading_card_button/proc/handle_choice(choice, mob/user)
var/input_value
switch(choice)
if("Set Mana")
input_value = tgui_input_number(user, "Please input total mana", "Set mana", display_panel_ref.gems, display_panel_ref.gem_slots, 0)
if(!isnull(input_value))
display_panel_ref.gems = input_value
if("Set Mana Slots")
input_value = tgui_input_number(user, "Please input total mana slots", "Set mana slots", display_panel_ref.gem_slots, 10, 1)
if(input_value)
display_panel_ref.gem_slots = input_value
if("Next Turn")
if (display_panel_ref.gem_slots <= 9)
display_panel_ref.gem_slots += 1
display_panel_ref.gems = display_panel_ref.gem_slots
/obj/machinery/trading_card_button/proc/check_menu(mob/living/user)
if(!istype(user))
return FALSE
if(user.incapacitated() || !user.Adjacent(src))
return FALSE
return TRUE
/obj/machinery/trading_card_button/health
name = "life control panel"
desc = "A set of buttons that lets you keep track of your life shards when playing Tactical Game Cards."
icon_state = "health_buttons"
display_panel_type = /obj/effect/decal/trading_card_panel/health
panel_offset_x = -24
///Global list containing all options used for the TGC health button.
GLOBAL_LIST_EMPTY(tcgcard_health_bar_radial_choices)
/obj/machinery/trading_card_button/health/setup_global()
return GLOB.tcgcard_health_bar_radial_choices
/obj/machinery/trading_card_button/health/setup_radial()
var/radial_choices
radial_choices = GLOB.tcgcard_health_bar_radial_choices = list(
"Set Life" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_set_life"),
"Inflict Damage" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_damage"),
)
return radial_choices
/obj/machinery/trading_card_button/health/handle_choice(choice, mob/user)
var/input_value
switch(choice)
if("Set Life")
input_value = tgui_input_number(user, "Please input total life", "Set life", display_panel_ref.gems, display_panel_ref.gem_slots, 0)
if(!isnull(input_value))
display_panel_ref.gems = input_value
if("Inflict Damage")
display_panel_ref.gems -= tgui_input_number(user, "Please input total damage", "Inflict damage", 1, display_panel_ref.gem_slots, 0)
///A display panel that renders a set of icons (in this case mana crystals), this is generated by /obj/machinery/trading_card_button and can be manipulated by the button which generates it.
/obj/effect/decal/trading_card_panel
name = "mana panel"
icon = 'icons/obj/toys/tcgmisc_large.dmi'
icon_state = "display_panel"
pixel_x = -10
///How much "active" gems will appear
var/gems = 1
///How many "inactive" gems will appear
var/gem_slots = 1
///The maximum number of inactive or active gems that can be set.
var/max_gems = 10
///Where the gem bar is placed in relation to the centre of the panel on the Y axis.
var/gem_bar_offset_z = -11
///Where the gem bar is placed in relation to the centre of the panel on the X axis, useful for multi-column displays.
var/gem_bar_offset_w = 0
///The gap between each gem on the Y axis
var/individual_gem_offset_y = 5
///The gap between each gem on the X axis
var/individual_gem_offset_x = -10
///The maximum number of rows that can be displayed on the panel.
var/number_of_rows = 10
///The maximum number of columns that can be displayed on the panel.
var/number_of_columns = 1
///File that both icon states for gems are pulled from
var/gem_icon_file = 'icons/obj/toys/tcgmisc.dmi'
///The icon of an "active" gem.
var/gem_icon = "gem_blue"
///The icon of an "inactive" gem.
var/empty_gem_icon = "gem_blue_empty"
///The name of what this panel tracks, used in the description
var/gem_title = "mana"
/obj/effect/decal/trading_card_panel/Initialize(mapload)
. = ..()
update_icon(UPDATE_OVERLAYS)
/obj/effect/decal/trading_card_panel/update_overlays()
. = ..()
if(!gem_slots)
return
gems = clamp(gems, 0, gem_slots)
for(var/gem_row in 1 to number_of_rows)
for(var/gem in 1 to number_of_columns)
if(gem_slots >= (gem_row - 1) * number_of_columns + gem)
var/mutable_appearance/gem_overlay = mutable_appearance(gem_icon_file, empty_gem_icon)
if(gems >= (gem_row - 1) * number_of_columns + gem)
gem_overlay.icon_state = gem_icon
gem_overlay.pixel_z = gem_row * individual_gem_offset_y + gem_bar_offset_z
gem_overlay.pixel_w = (gem - 1) * individual_gem_offset_x + gem_bar_offset_w
. += gem_overlay
/obj/effect/decal/trading_card_panel/examine(mob/user)
. = ..()
. += span_notice("It is currently showing [gems] out of [gem_slots] [gem_title].")
///A variant of the display panel for life shards, this one is set up to display two columns.
/obj/effect/decal/trading_card_panel/health
name = "life shard panel"
pixel_x = 9
gems = 20
gem_slots = 20
max_gems = 20
gem_bar_offset_w = 3
individual_gem_offset_x = -5
number_of_columns = 2
gem_icon = "gem_red"
empty_gem_icon = "gem_red_empty"
gem_title = "life shards"