diff --git a/code/__DEFINES/art.dm b/code/__DEFINES/art.dm index 83341ad5251..b6e9b885d91 100644 --- a/code/__DEFINES/art.dm +++ b/code/__DEFINES/art.dm @@ -9,3 +9,8 @@ #define TAB_LIBRARY 1 #define TAB_SECURE 2 #define TAB_PRIVATE 3 + +///cost defines for drawing graffiti: how many charges of a crayon or spraycan are used. +#define CRAYON_COST_SMALL 0.5 +#define CRAYON_COST_DEFAULT 1 +#define CRAYON_COST_LARGE 5 diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index 7a336ec1d4e..03c680c88de 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -9,8 +9,9 @@ #define COMSIG_OBJ_DEFAULT_UNFASTEN_WRENCH "obj_default_unfasten_wrench" ///from base of /turf/proc/levelupdate(). (intact) true to hide and false to unhide #define COMSIG_OBJ_HIDE "obj_hide" -/// from /obj/item/toy/crayon/spraycan/afterattack: (color_is_dark) +/// from /obj/item/toy/crayon/spraycan/afterattack: (user, spraycan, color_is_dark) #define COMSIG_OBJ_PAINTED "obj_painted" + #define DONT_USE_SPRAYCAN_CHARGES (1<<0) // /obj/machinery signals diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm index 40171d3cddd..17ff9c6d695 100644 --- a/code/game/objects/effects/decals/crayon.dm +++ b/code/game/objects/effects/decals/crayon.dm @@ -29,6 +29,15 @@ if(main) paint_colour = main add_atom_colour(paint_colour, FIXED_COLOUR_PRIORITY) + RegisterSignal(src, COMSIG_OBJ_PAINTED, PROC_REF(on_painted)) /obj/effect/decal/cleanable/crayon/NeverShouldHaveComeHere(turf/T) return isgroundlessturf(T) + +/obj/effect/decal/cleanable/crayon/proc/on_painted(datum/source, mob/user, obj/item/toy/crayon/spraycan/spraycan, is_dark_color) + SIGNAL_HANDLER + var/cost = spraycan.all_drawables[icon_state] || CRAYON_COST_DEFAULT + if (HAS_TRAIT(user, TRAIT_TAGGER)) + cost *= 0.5 + spraycan.use_charges(user, cost, requires_full = FALSE) + return DONT_USE_SPRAYCAN_CHARGES diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 2cc0f536e4b..724b35b80b5 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -14,6 +14,8 @@ #define AVAILABLE_SPRAYCAN_SPACE 8 // enough to fill one radial menu page +#define INFINITE_CHARGES -1 + /* * Crayons */ @@ -49,7 +51,7 @@ /// Dictates how large of an area we cover with our paint var/paint_mode = PAINT_NORMAL - /// Number of times this item can be used, -1 for unlimited + /// Number of times this item can be used, INFINITE_CHARGES for unlimited var/charges = 30 /// Number of remaining charges var/charges_left @@ -83,7 +85,12 @@ /// Whether to play a sound after using var/post_noise = FALSE - /// List of selectable graffiti options + /** + * List of selectable graffiti options + * If an associated value is present, the graffiti has its own cost + * otherwise it'll be the default value. + * Ditto with the other other lists below. + */ var/static/list/graffiti = list( "amyjon", "antilizard", @@ -138,7 +145,7 @@ "largebrush", "scroll", "shotgun", - "smallbrush", + "smallbrush" = CRAYON_COST_SMALL, "snake", "splatter", "stickman", @@ -148,14 +155,14 @@ ) /// List of selectable orientable options var/static/list/oriented = list( - "arrow", + "arrow" = CRAYON_COST_SMALL, "body", - "chevron", - "clawprint", - "footprint", + "chevron" = CRAYON_COST_SMALL, + "clawprint" = CRAYON_COST_SMALL, + "footprint" = CRAYON_COST_SMALL, "line", - "pawprint", - "shortline", + "pawprint" = CRAYON_COST_SMALL, + "shortline" = CRAYON_COST_SMALL, "thinline", ) /// List of selectable rune options @@ -181,10 +188,10 @@ ) /// List of selectable large options var/static/list/graffiti_large_h = list( - "furrypride", - "paint", - "secborg", - "yiffhell", + "furrypride" = CRAYON_COST_LARGE, + "paint" = CRAYON_COST_LARGE, + "secborg" = CRAYON_COST_LARGE, + "yiffhell" = CRAYON_COST_LARGE, ) /// Combined lists var/static/list/all_drawables = graffiti + symbols + drawings + oriented + runes + graffiti_large_h @@ -216,7 +223,7 @@ update_appearance() /obj/item/toy/crayon/proc/refill() - if(charges == -1) + if(charges == INFINITE_CHARGES) charges_left = 100 else charges_left = charges @@ -237,15 +244,13 @@ /obj/item/toy/crayon/proc/use_charges(mob/user, amount = 1, requires_full = TRUE) // Returns number of charges actually used - if(charges == -1) - . = amount + if(charges == INFINITE_CHARGES) refill() - else - if(check_empty(user, amount, requires_full)) - return 0 - else - . = min(charges_left, amount) - charges_left -= . + return TRUE + if(check_empty(user, amount, requires_full)) + return FALSE + charges_left -= min(charges_left, amount) + return TRUE /obj/item/toy/crayon/proc/check_empty(mob/user, amount = 1, requires_full = TRUE) // When eating a crayon, check_empty() can be called twice producing @@ -253,8 +258,8 @@ if(QDELETED(src)) return TRUE - // -1 is unlimited charges - if(charges == -1) + // INFINITE_CHARGES is unlimited charges + if(charges == INFINITE_CHARGES) return FALSE if(!charges_left) balloon_alert(user, "empty!") @@ -387,28 +392,16 @@ /// Attempts to color the target. Returns how many charges were used. /obj/item/toy/crayon/proc/use_on(atom/target, mob/user, params) var/static/list/punctuation = list("!","?",".",",","/","+","-","=","%","#","&") - var/istagger = HAS_TRAIT(user, TRAIT_TAGGER) - - var/cost = 1 - if(paint_mode == PAINT_LARGE_HORIZONTAL) - cost = 5 - if(istype(target, /obj/item/canvas)) - cost = 0 - if(ishuman(user)) - if (istagger) - cost *= 0.5 - if(check_empty(user, cost)) - return 0 if(istype(target, /obj/effect/decal/cleanable)) target = target.loc if(!isturf(target)) - return 0 + return if(!isValidSurface(target)) target.balloon_alert(user, "can't use there!") - return 0 + return var/drawing = drawtype switch(drawtype) @@ -431,6 +424,14 @@ if(RANDOM_ANY) drawing = pick(all_drawables) + var/istagger = HAS_TRAIT(user, TRAIT_TAGGER) + var/cost = all_drawables[drawing] || CRAYON_COST_DEFAULT + if(istype(target, /obj/item/canvas)) + cost = 0 + if (istagger) + cost *= 0.5 + if(check_empty(user, cost)) + return var/temp = "rune" var/ascii = (length(drawing) == 1) @@ -478,14 +479,11 @@ if(paint_mode == PAINT_LARGE_HORIZONTAL) wait_time *= 3 - if(!instant) - if(!do_after(user, 50, target = target)) - return 0 + if(!instant && !do_after(user, 50, target = target)) + return - var/charges_used = use_charges(user, cost) - if(!charges_used) - return 0 - . = charges_used + if(!use_charges(user, cost)) + return if(length(text_buffer)) drawing = text_buffer[1] @@ -536,22 +534,20 @@ for(var/turf/draw_turf as anything in affected_turfs) reagents.expose(draw_turf, methods = TOUCH, volume_modifier = volume_multiplier) check_empty(user) - return . /obj/item/toy/crayon/afterattack(atom/target, mob/user, proximity, params) . = ..() if(!proximity) - return . + return if (isitem(target)) . |= AFTERATTACK_PROCESSED_ITEM if (!check_allowed_items(target)) - return . + return use_on(target, user, params) - return . /obj/item/toy/crayon/attack(mob/target, mob/user) if(!edible || (target != user)) @@ -651,7 +647,7 @@ paint_color = "#FFFFFF" crayon_color = "mime" reagent_contents = list(/datum/reagent/consumable/nutriment = 0.5, /datum/reagent/colorful_reagent/powder/invisible = 1.5) - charges = -1 + charges = INFINITE_CHARGES dye_color = DYE_MIME /obj/item/toy/crayon/rainbow @@ -661,7 +657,7 @@ crayon_color = "rainbow" reagent_contents = list(/datum/reagent/consumable/nutriment = 0.5, /datum/reagent/colorful_reagent = 1.5) drawtype = RANDOM_ANY // just the default starter. - charges = -1 + charges = INFINITE_CHARGES dye_color = DYE_RAINBOW /obj/item/toy/crayon/rainbow/afterattack(atom/target, mob/user, proximity, params) @@ -765,7 +761,8 @@ /obj/item/toy/crayon/spraycan/suicide_act(mob/living/user) var/mob/living/carbon/human/H = user - if(is_capped || !actually_paints) + var/used = min(charges_left, 10) + if(is_capped || !actually_paints || !use_charges(user, 10, FALSE)) user.visible_message(span_suicide("[user] shakes up [src] with a rattle and lifts it to [user.p_their()] mouth, but nothing happens!")) user.say("MEDIOCRE!!", forced = "spraycan suicide") return SHAME @@ -779,7 +776,6 @@ update_appearance() if(actually_paints) H.update_lips("spray_face", paint_color) - var/used = use_charges(user, 10, FALSE) reagents.trans_to(user, used, volume_multiplier, transfered_by = user, methods = VAPOR) return OXYLOSS @@ -792,19 +788,20 @@ /obj/item/toy/crayon/spraycan/examine(mob/user) . = ..() - if(charges_left) - . += "It has [charges_left] use\s left." - else - . += "It is empty." + if(charges != INFINITE_CHARGES) + if(charges_left) + . += "It's roughly [PERCENT(charges_left/charges)]% full." + else + . += "It is empty." . += span_notice("Alt-click [src] to [ is_capped ? "take the cap off" : "put the cap on"]. Right-click a colored object to match its existing color.") /obj/item/toy/crayon/spraycan/use_on(atom/target, mob/user, params) if(is_capped) balloon_alert(user, "take the cap off first!") - return 0 + return if(check_empty(user)) - return 0 + return if(iscarbon(target)) if(pre_noise || post_noise) @@ -823,28 +820,31 @@ if(ishuman(carbon_target) && actually_paints) var/mob/living/carbon/human/human_target = carbon_target human_target.update_lips("spray_face", paint_color) - . = use_charges(user, 10, FALSE) + use_charges(user, 10, FALSE) var/fraction = min(1, . / reagents.maximum_volume) reagents.expose(carbon_target, VAPOR, fraction * volume_multiplier) - return . + else if(actually_paints && target.atom_colours[atom_colours] == paint_color) + balloon_alert(user, "[target.p_theyre()] already of that color.") + return FALSE if(ismob(target) && (HAS_TRAIT(target, TRAIT_SPRAY_PAINTABLE))) if(actually_paints) target.add_atom_colour(paint_color, WASHABLE_COLOUR_PRIORITY) SEND_SIGNAL(target, COMSIG_LIVING_MOB_PAINTED) - . = use_charges(user, 2, requires_full = FALSE) + use_charges(user, 2, requires_full = FALSE) reagents.trans_to(target, ., volume_multiplier, transfered_by = user, methods = VAPOR) if(pre_noise || post_noise) playsound(user.loc, 'sound/effects/spray.ogg', 5, TRUE, 5) user.visible_message(span_notice("[user] coats [target] with spray paint!"), span_notice("You coat [target] with spray paint.")) - return . + return if(isobj(target) && !(target.flags_1 & UNPAINTABLE_1)) + var/color_is_dark = FALSE if(actually_paints) - var/color_is_dark = is_color_dark(paint_color) + color_is_dark = is_color_dark(paint_color) if (color_is_dark && !(target.flags_1 & ALLOW_DARK_PAINTS_1)) to_chat(user, span_warning("A color that dark on an object like this? Surely not...")) @@ -858,14 +858,14 @@ holder.update_held_items() else holder.update_clothing(target_item.slot_flags) - SEND_SIGNAL(target, COMSIG_OBJ_PAINTED, color_is_dark) - . = use_charges(user, 2, requires_full = FALSE) + if(!(SEND_SIGNAL(target, COMSIG_OBJ_PAINTED, user, src, color_is_dark) & DONT_USE_SPRAYCAN_CHARGES)) + use_charges(user, 2, requires_full = FALSE) reagents.trans_to(target, ., volume_multiplier, transfered_by = user, methods = VAPOR) if(pre_noise || post_noise) playsound(user.loc, 'sound/effects/spray.ogg', 5, TRUE, 5) user.visible_message(span_notice("[user] coats [target] with spray paint!"), span_notice("You coat [target] with spray paint.")) - return 0 + return return ..() @@ -920,37 +920,21 @@ /obj/item/toy/crayon/spraycan/borg name = "cyborg spraycan" desc = "A metallic container containing shiny synthesised paint." - charges = -1 + charges = INFINITE_CHARGES -/obj/item/toy/crayon/spraycan/borg/afterattack(atom/target,mob/user,proximity, params) - if (!proximity) - return - - if (isitem(target)) - . = AFTERATTACK_PROCESSED_ITEM - - if (!check_allowed_items(target)) - return . - - var/diff = use_on(target, user, params) +/obj/item/toy/crayon/spraycan/borg/use_charges(mob/user, amount = 1, requires_full = TRUE) if(!iscyborg(user)) to_chat(user, span_notice("How did you get this?")) qdel(src) - return . + return FALSE var/mob/living/silicon/robot/borgy = user - - if(!diff) - return . // 25 is our cost per unit of paint, making it cost 25 energy per // normal tag, 50 per window, and 250 per attack - var/cost = diff * 25 - // Cyborgs shouldn't be able to use modules without a cell. But if they do - // it's free. - if(borgy.cell) - borgy.cell.use(cost) + if(!borgy.cell?.use(amount * 25)) + return FALSE + return ..() - return . /obj/item/toy/crayon/spraycan/hellcan name = "hellcan" @@ -1002,7 +986,7 @@ /obj/item/toy/crayon/spraycan/infinite name = "infinite spraycan" - charges = -1 + charges = INFINITE_CHARGES desc = "Now with 30% more bluespace technology." #undef RANDOM_GRAFFITI @@ -1020,3 +1004,5 @@ #undef PAINT_NORMAL #undef PAINT_LARGE_HORIZONTAL #undef PAINT_LARGE_HORIZONTAL_ICON + +#undef INFINITE_CHARGES diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index ca68a00adfb..ea332049e3c 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -361,8 +361,10 @@ /obj/structure/window/proc/AfterRotation(mob/user, degrees) air_update_turf(TRUE, FALSE) -/obj/structure/window/proc/on_painted(obj/structure/window/source, is_dark_color) +/obj/structure/window/proc/on_painted(obj/structure/window/source, mob/user, obj/item/toy/crayon/spraycan/spraycan, is_dark_color) SIGNAL_HANDLER + if(!spraycan.actually_paints) + return if (is_dark_color && fulltile) //Opaque directional windows restrict vision even in directions they are not placed in, please don't do this set_opacity(255) else diff --git a/code/modules/power/floodlight.dm b/code/modules/power/floodlight.dm index ed35495106e..bb2e07fb45b 100644 --- a/code/modules/power/floodlight.dm +++ b/code/modules/power/floodlight.dm @@ -150,8 +150,10 @@ RegisterSignal(src, COMSIG_OBJ_PAINTED, TYPE_PROC_REF(/obj/machinery/power/floodlight, on_color_change)) //update light color when color changes register_context() -/obj/machinery/power/floodlight/proc/on_color_change(obj/machinery/power/flood_light, is_dark_color) +/obj/machinery/power/floodlight/proc/on_color_change(obj/machinery/power/flood_light, mob/user, obj/item/toy/crayon/spraycan/spraycan, is_dark_color) SIGNAL_HANDLER + if(!spraycan.actually_paints) + return if(setting > FLOODLIGHT_OFF) update_light_state()