Files
nevimer baf3837ae8 Merge remote-tracking branch 'tgstation/master' into upstream-2025-11-29
# Conflicts:
#	_maps/RandomRuins/SpaceRuins/derelict_sulaco.dmm
#	_maps/RandomRuins/SpaceRuins/garbagetruck2.dmm
#	_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm
#	_maps/map_files/tramstation/tramstation.dmm
#	code/_onclick/hud/new_player.dm
#	code/datums/components/squashable.dm
#	code/datums/diseases/advance/symptoms/heal.dm
#	code/datums/diseases/chronic_illness.dm
#	code/datums/status_effects/buffs.dm
#	code/datums/status_effects/debuffs/drunk.dm
#	code/datums/status_effects/debuffs/stamcrit.dm
#	code/game/machinery/computer/crew.dm
#	code/game/objects/items/devices/scanners/health_analyzer.dm
#	code/game/objects/items/wall_mounted.dm
#	code/game/turfs/closed/indestructible.dm
#	code/modules/admin/view_variables/filterrific.dm
#	code/modules/antagonists/heretic/influences.dm
#	code/modules/cargo/orderconsole.dm
#	code/modules/client/preferences.dm
#	code/modules/events/space_vines/vine_mutations.dm
#	code/modules/mob/dead/new_player/new_player.dm
#	code/modules/mob/living/carbon/human/death.dm
#	code/modules/mob/living/carbon/human/species_types/jellypeople.dm
#	code/modules/mob/living/damage_procs.dm
#	code/modules/mob/living/living.dm
#	code/modules/mob_spawn/ghost_roles/mining_roles.dm
#	code/modules/mob_spawn/mob_spawn.dm
#	code/modules/projectiles/ammunition/energy/laser.dm
#	code/modules/projectiles/guns/ballistic/launchers.dm
#	code/modules/projectiles/guns/energy/laser.dm
#	code/modules/reagents/chemistry/machinery/chem_dispenser.dm
#	code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm
#	code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
#	code/modules/reagents/chemistry/reagents/medicine_reagents.dm
#	code/modules/surgery/healing.dm
#	code/modules/unit_tests/designs.dm
#	icons/mob/inhands/items_lefthand.dmi
#	icons/mob/inhands/items_righthand.dmi
#	tgui/packages/tgui/interfaces/ChemDispenser.tsx
2025-11-29 22:49:21 -05:00

166 lines
5.4 KiB
Plaintext

#define STATUE_FILTER "statue_filter"
#define FILTER_COLOR "#34b347"
#define RECALL_DURATION 3 SECONDS
#define MINIMUM_COLOR_VALUE 20
/obj/item/frog_statue
name = "frog statue"
desc = "Are they really comfortable living in this thing?"
icon = 'icons/obj/weapons/guns/magic.dmi'
icon_state = "frog_statue"
item_flags = NOBLUDGEON
///our pet frog
var/mob/living/contained_frog
///the summon cooldown
COOLDOWN_DECLARE(summon_cooldown)
/obj/item/frog_statue/attack_self(mob/user)
. = ..()
if(.)
return TRUE
if(!COOLDOWN_FINISHED(src, summon_cooldown))
user.balloon_alert(user, "recharging!")
return TRUE
COOLDOWN_START(src, summon_cooldown, 30 SECONDS)
if(isnull(contained_frog))
user.balloon_alert(user, "no frog linked!")
return TRUE
if(contained_frog.loc == src)
release_frog(user)
return TRUE
recall_frog(user)
return TRUE
/obj/item/frog_statue/examine(mob/user)
. = ..()
if(!IS_WIZARD(user))
return
if(isnull(contained_frog))
. += span_notice("There are currently no frogs linked to this statue!")
else
. += span_notice("Using it will [(contained_frog in src) ? "release" : "recall"] the beast!")
///resummon the frog into its home
/obj/item/frog_statue/proc/recall_frog(mob/user)
playsound(src, 'sound/items/frog_statue_release.ogg', 20)
user.Beam(contained_frog, icon_state = "lichbeam", time = RECALL_DURATION)
animate(contained_frog, transform = matrix().Scale(0.3, 0.3), time = RECALL_DURATION)
addtimer(CALLBACK(contained_frog, TYPE_PROC_REF(/atom/movable, forceMove), src), RECALL_DURATION)
///release the frog to wreak havoc
/obj/item/frog_statue/proc/release_frog(mob/user)
var/list/possible_turfs = list()
for(var/turf/possible_turf in oview(2, user))
if(possible_turf.is_blocked_turf() || isopenspaceturf(possible_turf))
continue
possible_turfs += possible_turf
playsound(src, 'sound/items/frog_statue_release.ogg', 50, TRUE)
var/turf/final_turf = length(possible_turfs) ? pick(possible_turfs) : get_turf(src)
user.Beam(final_turf, icon_state = "lichbeam", time = RECALL_DURATION)
contained_frog.forceMove(final_turf)
animate(contained_frog, transform = matrix(), time = RECALL_DURATION)
REMOVE_TRAIT(contained_frog, TRAIT_AI_PAUSED, MAGIC_TRAIT)
///set this frog as our inhabitor
/obj/item/frog_statue/proc/set_new_frog(mob/living/frog)
frog.transform = frog.transform.Scale(0.3, 0.3)
contained_frog = frog
animate_filter()
RegisterSignal(frog, COMSIG_QDELETING, PROC_REF(render_obsolete))
/// we have lost our frog, let out a scream!
/obj/item/frog_statue/proc/render_obsolete(datum/source)
SIGNAL_HANDLER
contained_frog = null
playsound(src, 'sound/effects/magic/demon_dies.ogg', 50, TRUE)
UnregisterSignal(source, COMSIG_QDELETING)
/obj/item/frog_statue/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
if(arrived != contained_frog)
return
animate_filter()
ADD_TRAIT(contained_frog, TRAIT_AI_PAUSED, MAGIC_TRAIT)
if(contained_frog.health < contained_frog.maxHealth)
START_PROCESSING(SSobj, src)
/obj/item/frog_statue/process(seconds_per_tick)
if(isnull(contained_frog))
return
if(contained_frog.health == contained_frog.maxHealth)
STOP_PROCESSING(SSobj, src)
return
if(contained_frog.stat == DEAD)
contained_frog.revive()
contained_frog.adjust_brute_loss(-5)
/obj/item/frog_statue/proc/animate_filter(mob/living/frog)
add_filter(STATUE_FILTER, 2, list("type" = "outline", "color" = FILTER_COLOR, "size" = 1))
var/filter = get_filter(STATUE_FILTER)
animate(filter, alpha = 230, time = 2 SECONDS, loop = -1)
animate(alpha = 30, time = 0.5 SECONDS)
/obj/item/frog_statue/Exited(atom/movable/gone, direction)
. = ..()
if(gone != contained_frog)
return
clear_filters()
/obj/item/frog_contract
name = "frog contract"
desc = "Create a pact with an elder frog! This great beast will be your mount, protector, but most importantly your friend."
icon = 'icons/obj/scrolls.dmi'
icon_state = "scroll"
/obj/item/frog_contract/attack_self(mob/user)
. = ..()
if(.)
return TRUE
create_frog(user)
return TRUE
///customize our own frog and trap it into the statue
/obj/item/frog_contract/proc/create_frog(mob/user)
var/obj/item/frog_statue/statue = new(null)
var/mob/living/basic/leaper/new_frog = new(statue)
statue.set_new_frog(new_frog)
new_frog.befriend(user)
ADD_TRAIT(new_frog, TRAIT_AI_PAUSED, MAGIC_TRAIT)
select_frog_name(user, new_frog)
select_frog_color(user, new_frog)
user.put_in_hands(statue)
qdel(src)
/obj/item/frog_contract/proc/select_frog_name(mob/user, mob/new_frog)
var/frog_name = sanitize_name(tgui_input_text(user, "Choose your frog's name!", "Name pet toad", "leaper", MAX_NAME_LEN), allow_numbers = TRUE)
if(!frog_name)
to_chat(user, span_warning("Please enter a valid name."))
select_frog_name(user, new_frog)
return
new_frog.name = frog_name
/obj/item/frog_contract/proc/select_frog_color(mob/user, mob/living/basic/leaper/new_frog)
var/frog_color = tgui_color_picker(user, "Select your frog's color!" , "Pet toad color", COLOR_GREEN) // BUBBERSTATION EDIT: TGUI COLOR PICKER
if(isnull(frog_color))
to_chat(user, span_warning("Please choose a valid color."))
select_frog_color(user, new_frog)
return
var/list/hsv_frog = rgb2hsv(frog_color)
if(hsv_frog[3] < MINIMUM_COLOR_VALUE)
to_chat(user, span_danger("This color is too dark!"))
select_frog_color(user, new_frog)
return
new_frog.set_color_overlay(frog_color)
#undef STATUE_FILTER
#undef FILTER_COLOR
#undef RECALL_DURATION
#undef MINIMUM_COLOR_VALUE