diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm index 3cce288e67..0070362f8b 100644 --- a/code/_onclick/hud/radial.dm +++ b/code/_onclick/hud/radial.dm @@ -194,6 +194,9 @@ GLOBAL_LIST_EMPTY(radial_menus) else if(istext(choices_values[choice_id])) E.name = choices_values[choice_id] + else if(ispath(choices_values[choice_id],/atom)) + var/atom/A = choices_values[choice_id] + E.name = initial(A.name) else var/atom/movable/AM = choices_values[choice_id] //Movables only E.name = AM.name diff --git a/code/controllers/subsystem/materials.dm b/code/controllers/subsystem/materials.dm index 2134be0176..d8362ea0d1 100644 --- a/code/controllers/subsystem/materials.dm +++ b/code/controllers/subsystem/materials.dm @@ -25,7 +25,7 @@ SUBSYSTEM_DEF(materials) ) ///List of stackcrafting recipes for materials using rigid recipes var/list/rigid_stack_recipes = list( - // new /datum/stack_recipe("Carving block", /obj/structure/carving_block, 5, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE), + new /datum/stack_recipe("Carving block", /obj/structure/carving_block, 5, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE), ) ///Ran on initialize, populated the materials and materials_by_category dictionaries with their appropiate vars (See these variables for more info) diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm index a285b7d3f2..faca18caff 100644 --- a/code/datums/components/squeak.dm +++ b/code/datums/components/squeak.dm @@ -42,6 +42,8 @@ RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/on_drop) if(istype(parent, /obj/item/clothing/shoes)) RegisterSignal(parent, COMSIG_SHOES_STEP_ACTION, .proc/step_squeak) + else if(isstructure(parent)) + RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, .proc/use_squeak) override_squeak_sounds = custom_sounds if(chance_override) diff --git a/code/datums/elements/art.dm b/code/datums/elements/art.dm index 960d22af56..47908a2e2d 100644 --- a/code/datums/elements/art.dm +++ b/code/datums/elements/art.dm @@ -54,11 +54,14 @@ /datum/element/art/rev -/datum/element/art/rev/apply_moodlet(atom/source, mob/M, impress) - M.visible_message("[M] stops to inspect [source].", \ - "You take in [source], inspecting the fine craftsmanship of the proletariat.") - - if(M.mind && M.mind.has_antag_datum(/datum/antagonist/rev)) - SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "artgreat", /datum/mood_event/artgreat) +/datum/element/art/rev/apply_moodlet(atom/source, mob/user, impress) + var/msg + if(user.mind?.has_antag_datum(/datum/antagonist/rev)) + SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "artgreat", /datum/mood_event/artgreat) + msg = "What \a [pick("masterpiece", "chef-d'oeuvre")] [source.p_theyre()]. So [pick("subversive", "revolutionary", "unitizing", "egalitarian")]!" else - SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "artbad", /datum/mood_event/artbad) + SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "artbad", /datum/mood_event/artbad) + msg = "Wow, [source.p_they()] sucks." + + user.visible_message(span_notice("[user] stops to inspect [source]."), \ + span_notice("You appraise [source], inspecting the fine craftsmanship of the proletariat... [msg]")) diff --git a/code/datums/progressbar.dm b/code/datums/progressbar.dm index c2e0d3c9d2..b3d7d2d633 100644 --- a/code/datums/progressbar.dm +++ b/code/datums/progressbar.dm @@ -1,67 +1,139 @@ #define PROGRESSBAR_HEIGHT 6 +#define PROGRESSBAR_ANIMATION_TIME 5 /datum/progressbar - var/goal = 1 + ///The progress bar visual element. var/image/bar - var/shown = 0 + ///The target where this progress bar is applied and where it is shown. + var/atom/bar_loc + ///The mob whose client sees the progress bar. var/mob/user - var/client/client - var/listindex + ///The client seeing the progress bar. + var/client/user_client + ///Effectively the number of steps the progress bar will need to do before reaching completion. + var/goal = 1 + ///Control check to see if the progress was interrupted before reaching its goal. + var/last_progress = 0 + ///Variable to ensure smooth visual stacking on multiple progress bars. + var/listindex = 0 + /datum/progressbar/New(mob/User, goal_number, atom/target) . = ..() if (!istype(target)) - CRASH("Invalid target given") - if (goal_number) - goal = goal_number - bar = image('icons/effects/progessbar.dmi', target, "prog_bar_0", HUD_LAYER) - bar.plane = HUD_PLANE + EXCEPTION("Invalid target given") + if(QDELETED(User) || !istype(User)) + stack_trace("/datum/progressbar created with [isnull(User) ? "null" : "invalid"] user") + qdel(src) + return + if(!isnum(goal_number)) + stack_trace("/datum/progressbar created with [isnull(User) ? "null" : "invalid"] goal_number") + qdel(src) + return + goal = goal_number + bar_loc = target + bar = image('icons/effects/progessbar.dmi', bar_loc, "prog_bar_0") + bar.plane = ABOVE_HUD_PLANE bar.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA user = User - if(user) - client = user.client - LAZYINITLIST(user.progressbars) - LAZYINITLIST(user.progressbars[bar.loc]) - var/list/bars = user.progressbars[bar.loc] - bars.Add(src) + LAZYADDASSOCLIST(user.progressbars, bar_loc, src) + var/list/bars = user.progressbars[bar_loc] listindex = bars.len - bar.pixel_y = 32 + (PROGRESSBAR_HEIGHT * (listindex - 1)) -/datum/progressbar/proc/update(progress) - if (!user || !user.client) - shown = 0 - return - if (user.client != client) - if (client) - client.images -= bar - if (user.client) - user.client.images += bar + if(user.client) + user_client = user.client + add_prog_bar_image_to_client() - progress = clamp(progress, 0, goal) - bar.icon_state = "prog_bar_[round(((progress / goal) * 100), 5)]" - if (!shown) - user.client.images += bar - shown = 1 - -/datum/progressbar/proc/shiftDown() - --listindex - bar.pixel_y -= PROGRESSBAR_HEIGHT + RegisterSignal(user, COMSIG_PARENT_QDELETING, .proc/on_user_delete) + RegisterSignal(user, COMSIG_MOB_CLIENT_LOGOUT, .proc/clean_user_client) + RegisterSignal(user, COMSIG_MOB_CLIENT_LOGIN, .proc/on_user_login) /datum/progressbar/Destroy() - for(var/I in user.progressbars[bar.loc]) - var/datum/progressbar/P = I - if(P != src && P.listindex > listindex) - P.shiftDown() + if(user) + for(var/pb in user.progressbars[bar_loc]) + var/datum/progressbar/progress_bar = pb + if(progress_bar == src || progress_bar.listindex <= listindex) + continue + progress_bar.listindex-- - var/list/bars = user.progressbars[bar.loc] - bars.Remove(src) - if(!bars.len) - LAZYREMOVE(user.progressbars, bar.loc) + progress_bar.bar.pixel_y = 32 + (PROGRESSBAR_HEIGHT * (progress_bar.listindex - 1)) + var/dist_to_travel = 32 + (PROGRESSBAR_HEIGHT * (progress_bar.listindex - 1)) - PROGRESSBAR_HEIGHT + animate(progress_bar.bar, pixel_y = dist_to_travel, time = PROGRESSBAR_ANIMATION_TIME, easing = SINE_EASING) - if (client) - client.images -= bar - qdel(bar) - . = ..() + LAZYREMOVEASSOC(user.progressbars, bar_loc, src) + user = null + if(user_client) + clean_user_client() + + bar_loc = null + + if(bar) + QDEL_NULL(bar) + + return ..() + + +///Called right before the user's Destroy() +/datum/progressbar/proc/on_user_delete(datum/source) + SIGNAL_HANDLER + + user.progressbars = null //We can simply nuke the list and stop worrying about updating other prog bars if the user itself is gone. + user = null + qdel(src) + + +///Removes the progress bar image from the user_client and nulls the variable, if it exists. +/datum/progressbar/proc/clean_user_client(datum/source) + SIGNAL_HANDLER + + if(!user_client) //Disconnected, already gone. + return + user_client.images -= bar + user_client = null + + +///Called by user's Login(), it transfers the progress bar image to the new client. +/datum/progressbar/proc/on_user_login(datum/source) + SIGNAL_HANDLER + + if(user_client) + if(user_client == user.client) //If this was not client handling I'd condemn this sanity check. But clients are fickle things. + return + clean_user_client() + if(!user.client) //Clients can vanish at any time, the bastards. + return + user_client = user.client + add_prog_bar_image_to_client() + + +///Adds a smoothly-appearing progress bar image to the player's screen. +/datum/progressbar/proc/add_prog_bar_image_to_client() + bar.pixel_y = 0 + bar.alpha = 0 + user_client.images += bar + animate(bar, pixel_y = 32 + (PROGRESSBAR_HEIGHT * (listindex - 1)), alpha = 255, time = PROGRESSBAR_ANIMATION_TIME, easing = SINE_EASING) + + +///Updates the progress bar image visually. +/datum/progressbar/proc/update(progress) + progress = clamp(progress, 0, goal) + if(progress == last_progress) + return + last_progress = progress + bar.icon_state = "prog_bar_[round(((progress / goal) * 100), 5)]" + + +///Called on progress end, be it successful or a failure. Wraps up things to delete the datum and bar. +/datum/progressbar/proc/end_progress() + if(last_progress != goal) + bar.icon_state = "[bar.icon_state]_fail" + + animate(bar, alpha = 0, time = PROGRESSBAR_ANIMATION_TIME) + + QDEL_IN(src, PROGRESSBAR_ANIMATION_TIME) + + +#undef PROGRESSBAR_ANIMATION_TIME #undef PROGRESSBAR_HEIGHT diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm index b2bd394a53..eaf576510e 100644 --- a/code/game/objects/items/stacks/sheets/mineral.dm +++ b/code/game/objects/items/stacks/sheets/mineral.dm @@ -27,7 +27,6 @@ GLOBAL_LIST_INIT(sandstone_recipes, list ( \ new/datum/stack_recipe("pile of dirt", /obj/machinery/hydroponics/soil, 3, time = 10, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("sandstone door", /obj/structure/mineral_door/sandstone, 10, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("aesthetic volcanic floor tile", /obj/item/stack/tile/basalt, 2, 2, 4, 20), \ - new/datum/stack_recipe("Assistant Statue", /obj/structure/statue/sandstone/assistant, 5, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("Breakdown into sand", /obj/item/stack/ore/glass, 1, one_per_turf = 0, on_floor = 1) \ )) @@ -114,9 +113,6 @@ GLOBAL_LIST_INIT(sandbag_recipes, list ( \ GLOBAL_LIST_INIT(diamond_recipes, list ( \ new/datum/stack_recipe("diamond door", /obj/structure/mineral_door/transparent/diamond, 10, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("diamond tile", /obj/item/stack/tile/mineral/diamond, 1, 4, 20), \ - new/datum/stack_recipe("Captain Statue", /obj/structure/statue/diamond/captain, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("AI Hologram Statue", /obj/structure/statue/diamond/ai1, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("AI Core Statue", /obj/structure/statue/diamond/ai2, 5, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("diamond ingot", /obj/item/ingot/diamond, 6, time = 100), \ )) @@ -144,8 +140,6 @@ GLOBAL_LIST_INIT(diamond_recipes, list ( \ GLOBAL_LIST_INIT(uranium_recipes, list ( \ new/datum/stack_recipe("uranium door", /obj/structure/mineral_door/uranium, 10, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("uranium tile", /obj/item/stack/tile/mineral/uranium, 1, 4, 20), \ - new/datum/stack_recipe("Nuke Statue", /obj/structure/statue/uranium/nuke, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Engineer Statue", /obj/structure/statue/uranium/eng, 5, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("uranium ingot", /obj/item/ingot/uranium, 6, time = 100), \ )) @@ -178,7 +172,6 @@ GLOBAL_LIST_INIT(uranium_recipes, list ( \ GLOBAL_LIST_INIT(plasma_recipes, list ( \ new/datum/stack_recipe("plasma door", /obj/structure/mineral_door/transparent/plasma, 10, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("plasma tile", /obj/item/stack/tile/mineral/plasma, 1, 4, 20), \ - new/datum/stack_recipe("Scientist Statue", /obj/structure/statue/plasma/scientist, 5, one_per_turf = 1, on_floor = 1), \ // new/datum/stack_recipe("plasma ingot", /obj/item/ingot/plasma, 6, time = 100), \ no )) @@ -218,12 +211,7 @@ GLOBAL_LIST_INIT(plasma_recipes, list ( \ GLOBAL_LIST_INIT(gold_recipes, list ( \ new/datum/stack_recipe("golden door", /obj/structure/mineral_door/gold, 10, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("gold tile", /obj/item/stack/tile/mineral/gold, 1, 4, 20), \ - new/datum/stack_recipe("HoS Statue", /obj/structure/statue/gold/hos, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("HoP Statue", /obj/structure/statue/gold/hop, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("CE Statue", /obj/structure/statue/gold/ce, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("RD Statue", /obj/structure/statue/gold/rd, 5, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("Simple Crown", /obj/item/clothing/head/crown, 5), \ - new/datum/stack_recipe("CMO Statue", /obj/structure/statue/gold/cmo, 5, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("gold ingot", /obj/item/ingot/gold, 6, time = 100), \ )) @@ -251,11 +239,6 @@ GLOBAL_LIST_INIT(gold_recipes, list ( \ GLOBAL_LIST_INIT(silver_recipes, list ( \ new/datum/stack_recipe("silver door", /obj/structure/mineral_door/silver, 10, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("silver tile", /obj/item/stack/tile/mineral/silver, 1, 4, 20), \ - new/datum/stack_recipe("Med Officer Statue", /obj/structure/statue/silver/md, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Janitor Statue", /obj/structure/statue/silver/janitor, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Sec Officer Statue", /obj/structure/statue/silver/sec, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Sec Borg Statue", /obj/structure/statue/silver/secborg, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Med Borg Statue", /obj/structure/statue/silver/medborg, 5, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("silver ingot", /obj/item/ingot/silver, 6, time = 100), \ )) @@ -282,7 +265,6 @@ GLOBAL_LIST_INIT(silver_recipes, list ( \ GLOBAL_LIST_INIT(bananium_recipes, list ( \ new/datum/stack_recipe("bananium tile", /obj/item/stack/tile/mineral/bananium, 1, 4, 20), \ - new/datum/stack_recipe("Clown Statue", /obj/structure/statue/bananium/clown, 5, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("hilarious ingot", /obj/item/ingot/bananium, 6, time = 100), \ )) diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm index 2924545cc1..268838dac9 100644 --- a/code/game/objects/structures/statues.dm +++ b/code/game/objects/structures/statues.dm @@ -6,43 +6,59 @@ density = TRUE anchored = FALSE max_integrity = 100 - var/oreAmount = 5 - var/material_drop_type = /obj/item/stack/sheet/metal - var/impressiveness = 15 CanAtmosPass = ATMOS_PASS_DENSITY - + /// Beauty component mood modifier + var/impressiveness = 15 + /// Art component subtype added to this statue + var/art_type = /datum/element/art + /// Abstract root type + var/abstract_type = /obj/structure/statue /obj/structure/statue/Initialize() . = ..() - AddElement(/datum/element/art, impressiveness) - addtimer(CALLBACK(src, /datum.proc/_AddElement, list(/datum/element/beauty, impressiveness * 75)), 0) + AddElement(art_type, impressiveness) + AddElement(/datum/element/beauty, impressiveness * 75) + +/obj/structure/statue/ComponentInitialize() + . = ..() + var/rotation_flags = ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS + AddComponent(/datum/component/simple_rotation, rotation_flags, null, CALLBACK(src, .proc/can_be_rotated)) + +/obj/structure/statue/proc/can_be_rotated() + if(anchored) + to_chat(user, "[src] cannot be rotated while it is fastened to the floor!") + return FALSE + return TRUE /obj/structure/statue/attackby(obj/item/W, mob/living/user, params) add_fingerprint(user) if(!(flags_1 & NODECONSTRUCT_1)) if(default_unfasten_wrench(user, W)) return - if(W.tool_behaviour == TOOL_WELDER || istype(W, /obj/item/gun/energy/plasmacutter)) + if(W.tool_behaviour == TOOL_WELDER) if(!W.tool_start_check(user, amount=0)) return FALSE - user.visible_message("[user] is slicing apart the [name].", \ - "You are slicing apart the [name]...") + user.visible_message(span_notice("[user] is slicing apart the [name]."), \ + span_notice("You are slicing apart the [name]...")) if(W.use_tool(src, user, 40, volume=50)) - user.visible_message("[user] slices apart the [name].", \ - "You slice apart the [name]!") + user.visible_message(span_notice("[user] slices apart the [name]."), \ + span_notice("You slice apart the [name]!")) deconstruct(TRUE) return return ..() +/obj/structure/statue/AltClick(mob/user) + return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation + /obj/structure/statue/deconstruct(disassembled = TRUE) if(!(flags_1 & NODECONSTRUCT_1)) - if(material_drop_type) - var/drop_amt = oreAmount - if(!disassembled) - drop_amt -= 2 - if(drop_amt > 0) - new material_drop_type(get_turf(src), drop_amt) + var/amount_mod = disassembled ? 0 : -2 + for(var/mat in custom_materials) + var/datum/material/custom_material = SSmaterials.GetMaterialRef(mat) + var/amount = max(0,round(custom_materials[mat]/MINERAL_MATERIAL_AMOUNT) + amount_mod) + if(amount > 0) + new custom_material.sheet_type(drop_location(),amount) qdel(src) //////////////////////////////////////STATUES///////////////////////////////////////////////////////////// @@ -51,10 +67,9 @@ /obj/structure/statue/uranium max_integrity = 300 light_range = 2 - material_drop_type = /obj/item/stack/sheet/mineral/uranium - var/last_event = 0 - var/active = null + custom_materials = list(/datum/material/uranium=MINERAL_MATERIAL_AMOUNT*5) impressiveness = 25 // radiation makes an impression + abstract_type = /obj/structure/statue/uranium /obj/structure/statue/uranium/nuke name = "statue of a nuclear fission explosive" @@ -66,39 +81,14 @@ desc = "This statue has a sickening green colour." icon_state = "eng" -/obj/structure/statue/uranium/attackby(obj/item/W, mob/user, params) - radiate() - return ..() - -/obj/structure/statue/uranium/Bumped(atom/movable/AM) - radiate() - ..() - -/obj/structure/statue/uranium/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) - radiate() - . = ..() - -/obj/structure/statue/uranium/attack_paw(mob/user) - radiate() - . = ..() - -/obj/structure/statue/uranium/proc/radiate() - if(!active) - if(world.time > last_event+15) - active = 1 - radiation_pulse(src, 30) - last_event = world.time - active = null - return - return - ////////////////////////////plasma/////////////////////////////////////////////////////////////////////// /obj/structure/statue/plasma max_integrity = 200 - material_drop_type = /obj/item/stack/sheet/mineral/plasma desc = "This statue is suitably made from plasma." impressiveness = 20 + custom_materials = list(/datum/material/plasma=MINERAL_MATERIAL_AMOUNT*5) + abstract_type = /obj/structure/statue/plasma /obj/structure/statue/plasma/scientist name = "statue of a scientist" @@ -108,7 +98,6 @@ if(exposed_temperature > 300) PlasmaBurn(exposed_temperature) - /obj/structure/statue/plasma/bullet_act(obj/item/projectile/Proj) var/burn = FALSE if(!(Proj.nodamage) && Proj.damage_type == BURN && !QDELETED(src)) @@ -136,7 +125,9 @@ /obj/structure/statue/plasma/proc/PlasmaBurn(exposed_temperature) if(QDELETED(src)) return - atmos_spawn_air("plasma=[oreAmount*10];TEMP=[exposed_temperature]") + if(custom_materials[/datum/material/plasma]) + var/plasma_amount = round(custom_materials[/datum/material/plasma]/MINERAL_MATERIAL_AMOUNT) + atmos_spawn_air("plasma=[plasma_amount*10];TEMP=[exposed_temperature]") deconstruct(FALSE) /obj/structure/statue/plasma/proc/ignite(exposed_temperature) @@ -147,9 +138,10 @@ /obj/structure/statue/gold max_integrity = 300 - material_drop_type = /obj/item/stack/sheet/mineral/gold desc = "This is a highly valuable statue made from gold." impressiveness = 30 + custom_materials = list(/datum/material/gold=MINERAL_MATERIAL_AMOUNT*5) + abstract_type = /obj/structure/statue/gold /obj/structure/statue/gold/hos name = "statue of the head of security" @@ -175,9 +167,10 @@ /obj/structure/statue/silver max_integrity = 300 - material_drop_type = /obj/item/stack/sheet/mineral/silver desc = "This is a valuable statue made from silver." impressiveness = 25 + custom_materials = list(/datum/material/silver=MINERAL_MATERIAL_AMOUNT*5) + abstract_type = /obj/structure/statue/silver /obj/structure/statue/silver/md name = "statue of a medical officer" @@ -203,9 +196,10 @@ /obj/structure/statue/diamond max_integrity = 1000 - material_drop_type = /obj/item/stack/sheet/mineral/diamond desc = "This is a very expensive diamond statue." impressiveness = 60 + custom_materials = list(/datum/material/diamond=MINERAL_MATERIAL_AMOUNT*5) + abstract_type = /obj/structure/statue/diamond /obj/structure/statue/diamond/captain name = "statue of THE captain." @@ -223,43 +217,22 @@ /obj/structure/statue/bananium max_integrity = 300 - material_drop_type = /obj/item/stack/sheet/mineral/bananium desc = "A bananium statue with a small engraving:'HOOOOOOONK'." - var/spam_flag = 0 impressiveness = 65 + custom_materials = list(/datum/material/bananium=MINERAL_MATERIAL_AMOUNT*5) + abstract_type = /obj/structure/statue/bananium /obj/structure/statue/bananium/clown name = "statue of a clown" icon_state = "clown" -/obj/structure/statue/bananium/Bumped(atom/movable/AM) - honk() - ..() - -/obj/structure/statue/bananium/attackby(obj/item/W, mob/user, params) - honk() - return ..() - -/obj/structure/statue/bananium/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) - honk() - . = ..() - -/obj/structure/statue/bananium/attack_paw(mob/user) - honk() - ..() - -/obj/structure/statue/bananium/proc/honk() - if(!spam_flag) - spam_flag = 1 - playsound(src.loc, 'sound/items/bikehorn.ogg', 50, 1) - spawn(20) - spam_flag = 0 - /////////////////////sandstone///////////////////////////////////////// /obj/structure/statue/sandstone max_integrity = 50 - material_drop_type = /obj/item/stack/sheet/mineral/sandstone + impressiveness = 15 + custom_materials = list(/datum/material/sandstone=MINERAL_MATERIAL_AMOUNT*5) + abstract_type = /obj/structure/statue/sandstone /obj/structure/statue/sandstone/assistant name = "statue of an assistant" @@ -277,9 +250,326 @@ /obj/structure/statue/snow max_integrity = 50 - material_drop_type = /obj/item/stack/sheet/mineral/snow + custom_materials = list(/datum/material/snow=MINERAL_MATERIAL_AMOUNT*5) + abstract_type = /obj/structure/statue/snow /obj/structure/statue/snow/snowman name = "snowman" desc = "Several lumps of snow put together to form a snowman." icon_state = "snowman" + +/obj/structure/statue/snow/snowlegion + name = "snowlegion" + desc = "Looks like that weird kid with the tiger plushie has been round here again." + icon_state = "snowlegion" + +///////////////////////////////bronze/////////////////////////////////// + +/obj/structure/statue/bronze + custom_materials = list(/datum/material/bronze=MINERAL_MATERIAL_AMOUNT*5) + abstract_type = /obj/structure/statue/bronze + +/obj/structure/statue/bronze/marx + name = "\improper Karl Marx bust" + desc = "A bust depicting a certain 19th century economist. You get the feeling a specter is haunting the station." + icon_state = "marx" + art_type = /datum/element/art/rev + +///////////Elder Atmosian/////////////////////////////////////////// +/* Yeah no. +/obj/structure/statue/elder_atmosian + name = "Elder Atmosian" + desc = "A statue of an Elder Atmosian, capable of bending the laws of thermodynamics to their will" + icon_state = "eng" + custom_materials = list(/datum/material/metalhydrogen = MINERAL_MATERIAL_AMOUNT*10) + max_integrity = 1000 + impressiveness = 100 + abstract_type = /obj/structure/statue/elder_atmosian //This one is uncarvable +*/ + +/obj/item/chisel + name = "chisel" + desc = "Breaking and making art since 4000 BC. This one uses advanced technology to allow creation of lifelike moving statues." + icon = 'icons/obj/statue.dmi' + icon_state = "chisel" + item_state = "screwdriver_nuke" + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + flags_1 = CONDUCT_1 + slot_flags = ITEM_SLOT_BELT + force = 5 + w_class = WEIGHT_CLASS_TINY + throwforce = 5 + throw_speed = 3 + throw_range = 5 + custom_materials = list(/datum/material/iron=75) + attack_verb = list("stabs") + hitsound = 'sound/weapons/bladeslice.ogg' + usesound = list('sound/items/screwdriver.ogg', 'sound/items/screwdriver2.ogg') + drop_sound = 'sound/items/handling/screwdriver_drop.ogg' + pickup_sound = 'sound/items/handling/screwdriver_pickup.ogg' + // item_flags = EYE_STAB // don't have it + sharpness = SHARP_POINTY + + /// Block we're currently carving in + var/obj/structure/carving_block/prepared_block + /// If tracked user moves we stop sculpting + var/mob/living/tracked_user + /// Currently sculpting + var/sculpting = FALSE + +/obj/item/chisel/Initialize(mapload) + . = ..() + //AddElement(/datum/element/eyestab) + //AddElement(/datum/element/wall_engraver) + //deals 200 damage to statues, meaning you can actually kill one in ~250 hits + +/obj/item/chisel/ComponentInitialize() + . = ..() + AddComponent(/datum/component/bane, /mob/living/simple_animal/hostile/statue, damage_multiplier = 40) + +/obj/item/chisel/Destroy() + prepared_block = null + tracked_user = null + return ..() + +/* +Hit the block to start +Point with the chisel at the target to choose what to sculpt or hit block to choose from preset statue types. +Hit block again to start sculpting. +Moving interrupts +*/ +/obj/item/chisel/pre_attack(atom/A, mob/living/user, params) + . = ..() + if(sculpting) + return + if(istype(A,/obj/structure/carving_block)) + if(A == prepared_block && (prepared_block.current_target || prepared_block.current_preset_type)) + start_sculpting(user) + else if(!prepared_block) + set_block(A,user) + else if(A == prepared_block) + show_generic_statues_prompt(user) + return TRUE + else if(prepared_block) //We're aiming at something next to us with block prepared + prepared_block.set_target(A,user) + return TRUE + +// We aim at something distant. +/obj/item/chisel/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(!proximity_flag && !sculpting && prepared_block && ismovable(target) && prepared_block.completion == 0) + prepared_block.set_target(target,user) + +/obj/item/chisel/proc/start_sculpting(mob/living/user) + to_chat(user,span_notice("You start sculpting [prepared_block]."),type="info") + sculpting = TRUE + //How long whole process takes + var/sculpting_time = 30 SECONDS + //Single interruptible progress period + var/sculpting_period = round(sculpting_time / world.icon_size) //this is just so it reveals pixels line by line for each. + var/interrupted = FALSE + var/remaining_time = sculpting_time - (prepared_block.completion * sculpting_time) + + var/datum/progressbar/total_progress_bar = new(user, sculpting_time, prepared_block ) + while(remaining_time > 0 && !interrupted) + if(do_after(user,sculpting_period, target = prepared_block, progress = FALSE)) + remaining_time -= sculpting_period + prepared_block.set_completion((sculpting_time - remaining_time)/sculpting_time) + total_progress_bar.update(sculpting_time - remaining_time) + else + interrupted = TRUE + total_progress_bar.end_progress() + if(!interrupted && !QDELETED(prepared_block)) + prepared_block.create_statue() + to_chat(user,span_notice("The statue is finished!"),type="info") + break_sculpting() + +/obj/item/chisel/proc/set_block(obj/structure/carving_block/B,mob/living/user) + prepared_block = B + tracked_user = user + RegisterSignal(tracked_user,COMSIG_MOVABLE_MOVED,.proc/break_sculpting) + to_chat(user,span_notice("You prepare to work on [B]."),type="info") + +/obj/item/chisel/dropped(mob/user, silent) + . = ..() + break_sculpting() + +/obj/item/chisel/proc/break_sculpting() + SIGNAL_HANDLER + sculpting = FALSE + if(prepared_block && prepared_block.completion == 0) + prepared_block.reset_target() + prepared_block = null + if(tracked_user) + UnregisterSignal(tracked_user,COMSIG_MOVABLE_MOVED) + tracked_user = null + +/obj/item/chisel/proc/show_generic_statues_prompt(mob/living/user) + var/list/choices = list() + for(var/statue_path in prepared_block.get_possible_statues()) + var/obj/structure/statue/S = statue_path + choices[statue_path] = image(icon=initial(S.icon),icon_state=initial(S.icon_state)) + var/choice = show_radial_menu(user, prepared_block , choices, require_near = TRUE) + if(choice) + prepared_block.current_preset_type = choice + var/image/chosen_looks = choices[choice] + prepared_block.current_target = chosen_looks.appearance + var/obj/structure/statue/S = choice + to_chat(user,span_notice("You decide to sculpt [prepared_block] into [initial(S.name)]."),type="info") + + +/obj/structure/carving_block + name = "block" + desc = "Ready for sculpting." + icon = 'icons/obj/statue.dmi' + icon_state = "block" + material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS | MATERIAL_ADD_PREFIX // MATERIAL_EFFECTS + density = TRUE + material_modifier = 0.5 //50% effectiveness of materials + + /// The thing it will look like - Unmodified resulting statue appearance + var/current_target + /// Currently chosen preset statue type + var/current_preset_type + //Table of required materials for each non-abstract statue type + var/static/list/statue_costs + /// statue completion from 0 to 1.0 + var/completion = 0 + /// Greyscaled target with cutout filter + var/mutable_appearance/target_appearance_with_filters + /// HSV color filters parameters + var/static/list/greyscale_with_value_bump = list(0,0,0, 0,0,0, 0,0,1, 0,0,-0.05) + +/obj/structure/carving_block/Destroy() + current_target = null + target_appearance_with_filters = null + return ..() + +/obj/structure/carving_block/proc/set_target(atom/movable/target,mob/living/user) + if(!is_viable_target(target)) + to_chat(user,"You won't be able to carve that.") + return + if(istype(target,/obj/structure/statue/custom)) + var/obj/structure/statue/custom/original = target + current_target = original.content_ma + else + current_target = target.appearance + var/mutable_appearance/ma = current_target + to_chat(user,span_notice("You decide to sculpt [src] into [ma.name]."),type="info") + +/obj/structure/carving_block/proc/reset_target() + current_target = null + current_preset_type = null + target_appearance_with_filters = null + +/obj/structure/carving_block/update_overlays() + . = ..() + if(!target_appearance_with_filters) + return + //We're only keeping one instance here that changes in the middle so we have to clone it to avoid managed overlay issues + var/mutable_appearance/clone = new(target_appearance_with_filters) + . += clone + +/obj/structure/carving_block/proc/is_viable_target(atom/movable/target) + //Only things on turfs + if(!isturf(target.loc)) + return FALSE + //No big icon things + var/icon/thing_icon = icon(target.icon, target.icon_state) + if(thing_icon.Height() != world.icon_size || thing_icon.Width() != world.icon_size) + return FALSE + return TRUE + +/obj/structure/carving_block/proc/create_statue() + if(current_preset_type) + var/obj/structure/statue/preset_statue = new current_preset_type(get_turf(src)) + preset_statue.set_custom_materials(custom_materials) + qdel(src) + else if(current_target) + var/obj/structure/statue/custom/new_statue = new(get_turf(src)) + new_statue.set_visuals(current_target) + new_statue.set_custom_materials(custom_materials) + var/mutable_appearance/ma = current_target + new_statue.name = "statue of [ma.name]" + new_statue.desc = "A statue depicting [ma.name]." + qdel(src) + +/obj/structure/carving_block/proc/set_completion(value) + if(!current_target) + return + if(!target_appearance_with_filters) + target_appearance_with_filters = new(current_target) + // KEEP_APART in case carving block gets KEEP_TOGETHER from somewhere like material texture filters. + target_appearance_with_filters.appearance_flags |= KEEP_TOGETHER | KEEP_APART + //Doesn't use filter helpers because MAs aren't atoms + target_appearance_with_filters.filters = filter(type="color",color=greyscale_with_value_bump,space=FILTER_COLOR_HSV) + completion = value + var/static/icon/white = icon('icons/effects/alphacolors.dmi', "white") + switch(value) + if(0) + //delete uncovered and reset filters + remove_filter("partial_uncover") + target_appearance_with_filters = null + else + var/mask_offset = min(world.icon_size,round(completion * world.icon_size)) + remove_filter("partial_uncover") + add_filter("partial_uncover", 1, alpha_mask_filter(icon = white, y = -mask_offset)) + target_appearance_with_filters.filters = filter(type="alpha",icon=white,y=-mask_offset,flags=MASK_INVERSE) + update_appearance() + + +/// Returns a list of preset statues carvable from this block depending on the custom materials +/obj/structure/carving_block/proc/get_possible_statues() + . = list() + if(!statue_costs) + statue_costs = build_statue_cost_table() + for(var/statue_path in statue_costs) + var/list/carving_cost = statue_costs[statue_path] + var/enough_materials = TRUE + for(var/required_material in carving_cost) + if(!custom_materials[required_material] || custom_materials[required_material] < carving_cost[required_material]) + enough_materials = FALSE + break + if(enough_materials) + . += statue_path + +/obj/structure/carving_block/proc/build_statue_cost_table() + . = list() + for(var/statue_type in subtypesof(/obj/structure/statue) - /obj/structure/statue/custom) + var/obj/structure/statue/S = new statue_type() + if(!S.icon_state || S.abstract_type == S.type || !S.custom_materials) + continue + .[S.type] = S.custom_materials + qdel(S) + +/obj/structure/statue/custom + name = "custom statue" + icon_state = "base" + obj_flags = CAN_BE_HIT | UNIQUE_RENAME + appearance_flags = TILE_BOUND | PIXEL_SCALE | KEEP_TOGETHER //Added keep together in case targets has weird layering + material_flags = MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS // MATERIAL_EFFECTS + /// primary statue overlay + var/mutable_appearance/content_ma + var/static/list/greyscale_with_value_bump = list(0,0,0, 0,0,0, 0,0,1, 0,0,-0.05) + +/obj/structure/statue/custom/Destroy() + content_ma = null + return ..() + +/obj/structure/statue/custom/proc/set_visuals(model_appearance) + if(content_ma) + QDEL_NULL(content_ma) + content_ma = new + content_ma.appearance = model_appearance + content_ma.pixel_x = 0 + content_ma.pixel_y = 0 + content_ma.alpha = 255 + content_ma.appearance_flags &= ~KEEP_APART //Don't want this + content_ma.filters = filter(type="color",color=greyscale_with_value_bump,space=FILTER_COLOR_HSV) + update_appearance() + +/obj/structure/statue/custom/update_overlays() + . = ..() + if(content_ma) + . += content_ma diff --git a/code/modules/research/designs/autolathe_desings/autolathe_designs_tools.dm b/code/modules/research/designs/autolathe_desings/autolathe_designs_tools.dm index bebf836ce0..63442ef1ac 100644 --- a/code/modules/research/designs/autolathe_desings/autolathe_designs_tools.dm +++ b/code/modules/research/designs/autolathe_desings/autolathe_designs_tools.dm @@ -158,3 +158,11 @@ materials = list(/datum/material/iron = 150, /datum/material/glass = 150) build_path = /obj/item/geiger_counter category = list("initial", "Tools") + +/datum/design/chisel + name = "Chisel" + id = "chisel" + build_type = AUTOLATHE + materials = list(/datum/material/iron = 75) + build_path = /obj/item/chisel + category = list("initial","Tools") diff --git a/code/modules/ruins/lavalandruin_code/elephantgraveyard.dm b/code/modules/ruins/lavalandruin_code/elephantgraveyard.dm index d9dfdd8c19..7dbb509779 100644 --- a/code/modules/ruins/lavalandruin_code/elephantgraveyard.dm +++ b/code/modules/ruins/lavalandruin_code/elephantgraveyard.dm @@ -3,27 +3,28 @@ /obj/structure/statue/bone anchored = TRUE max_integrity = 120 - material_drop_type = /obj/item/stack/sheet/bone impressiveness = 18 // Carved from the bones of a massive creature, it's going to be a specticle to say the least layer = ABOVE_ALL_MOB_LAYER + custom_materials = list(/datum/material/bone=MINERAL_MATERIAL_AMOUNT*5) + abstract_type = /obj/structure/statue/bone /obj/structure/statue/bone/rib name = "collosal rib" desc = "It's staggering to think that something this big could have lived, let alone died." - oreAmount = 4 + custom_materials = list(/datum/material/bone=MINERAL_MATERIAL_AMOUNT*4) icon = 'icons/obj/statuelarge.dmi' icon_state = "rib" /obj/structure/statue/bone/skull name = "collosal skull" desc = "The gaping maw of a dead, titanic monster." - oreAmount = 12 + custom_materials = list(/datum/material/bone=MINERAL_MATERIAL_AMOUNT*12) icon = 'icons/obj/statuelarge.dmi' icon_state = "skull" /obj/structure/statue/bone/skull/half desc = "The gaping maw of a dead, titanic monster. This one is cracked in half." - oreAmount = 6 + custom_materials = list(/datum/material/bone=MINERAL_MATERIAL_AMOUNT*6) icon = 'icons/obj/statuelarge.dmi' icon_state = "skull-half" diff --git a/icons/obj/statue.dmi b/icons/obj/statue.dmi index 0fe4f7f539..48368dda28 100644 Binary files a/icons/obj/statue.dmi and b/icons/obj/statue.dmi differ diff --git a/sound/items/handling/screwdriver_drop.ogg b/sound/items/handling/screwdriver_drop.ogg new file mode 100644 index 0000000000..d460fd0aed Binary files /dev/null and b/sound/items/handling/screwdriver_drop.ogg differ diff --git a/sound/items/handling/screwdriver_pickup.ogg b/sound/items/handling/screwdriver_pickup.ogg new file mode 100644 index 0000000000..368f1bfd27 Binary files /dev/null and b/sound/items/handling/screwdriver_pickup.ogg differ