diff --git a/code/datums/components/pinata.dm b/code/datums/components/pinata.dm new file mode 100644 index 00000000000..8f6866c5d94 --- /dev/null +++ b/code/datums/components/pinata.dm @@ -0,0 +1,62 @@ +///Objects or mobs with this componenet will drop items when taking damage. +/datum/component/pinata + ///How much damage does an attack need to do to have a chance to drop "candy" + var/minimum_damage + ///What is the likelyhood some "candy" should drop when attacked. + var/drop_chance + ///A list of "candy" items that can be dropped when taking damage + var/candy + ///Number of "candy" items dropped when the structure is destroyed/mob is killed, set to 0 if none. drop_chance and minimum damage are not applied. + var/death_drop + +/datum/component/pinata/Initialize( + minimum_damage = 10, + drop_chance = 40, + candy = list(/obj/item/food/candy, /obj/item/food/lollipop/cyborg, /obj/item/food/gumball, /obj/item/food/bubblegum, /obj/item/food/chocolatebar), + death_drop = 5, +) + + src.minimum_damage = minimum_damage + src.drop_chance = drop_chance + src.candy = candy + src.death_drop = death_drop + + if(ismob(parent)) + RegisterSignal(parent, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(damage_inflicted)) + RegisterSignal(parent, COMSIG_LIVING_DEATH, PROC_REF(pinata_broken)) + return + if(isobj(parent)) + var/obj/parent_integrity_test = parent + if(parent_integrity_test.uses_integrity) + RegisterSignal(parent, COMSIG_ATOM_TAKE_DAMAGE, PROC_REF(damage_inflicted)) + RegisterSignal(parent, COMSIG_ATOM_DESTRUCTION, PROC_REF(pinata_broken)) + return + return COMPONENT_INCOMPATIBLE + +/datum/component/pinata/proc/damage_inflicted(obj/target, damage, damage_type) + SIGNAL_HANDLER + if(damage < minimum_damage || damage_type == STAMINA || damage_type == OXY) + return + if(!prob(drop_chance + damage)) //Higher damage means less rolls but higher odds on the roll + return + var/list/turf_options = get_adjacent_open_turfs(parent) + turf_options += get_turf(parent) + if(length(turf_options)) + var/dropped_item = pick(candy) + new dropped_item(pick(turf_options)) + +/datum/component/pinata/proc/pinata_broken() + SIGNAL_HANDLER + for(var/i in 1 to death_drop) + var/dropped_item = pick(candy) + new dropped_item(get_turf(parent)) + qdel(src) + +/datum/component/pinata/Destroy(force, silent) + UnregisterSignal(parent, list( + COMSIG_MOB_APPLY_DAMAGE, + COMSIG_LIVING_DEATH, + COMSIG_ATOM_TAKE_DAMAGE, + COMSIG_ATOM_DESTRUCTION, + )) + return ..() diff --git a/code/game/machinery/computer/arcade/arcade.dm b/code/game/machinery/computer/arcade/arcade.dm index cc07e01a3a8..011086b1fb5 100644 --- a/code/game/machinery/computer/arcade/arcade.dm +++ b/code/game/machinery/computer/arcade/arcade.dm @@ -59,7 +59,7 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list( /obj/item/toy/plush/abductor = 2, /obj/item/toy/plush/abductor/agent = 2, /obj/item/toy/plush/greek_cucumber = 2, - /obj/item/storage/belt/military/snack = 2, + /obj/item/storage/belt/military/snack/full = 2, /obj/item/toy/brokenradio = 2, /obj/item/toy/braintoy = 2, /obj/item/toy/eldritch_book = 2, diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm index 69ec85dedb7..500f84cc757 100644 --- a/code/game/objects/effects/decals/cleanable/misc.dm +++ b/code/game/objects/effects/decals/cleanable/misc.dm @@ -253,6 +253,14 @@ icon = 'icons/obj/objects.dmi' icon_state = "paper_shreds" +/obj/effect/decal/cleanable/wrapping/pinata + name = "pinata shreds" + desc = "Torn pieces of papier-mâché, left over from a pinata" + icon_state = "pinata_shreds" + +/obj/effect/decal/cleanable/wrapping/pinata/syndie + icon_state = "syndie_pinata_shreds" + /obj/effect/decal/cleanable/garbage name = "decomposing garbage" desc = "A split open garbage bag, its stinking content seems to be partially liquified. Yuck!" diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index 3f432f09c86..047670a1f14 100644 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -525,9 +525,6 @@ . = ..() var/sponsor = pick("Donk Co.", "Waffle Co.", "Roffle Co.", "Gorlax Marauders", "Tiger Cooperative") desc = "A set of snack-tical webbing worn by athletes of the [sponsor] VR sports division." - -/obj/item/storage/belt/military/snack/Initialize(mapload) - . = ..() atom_storage.max_slots = 6 atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL atom_storage.set_holdable(list( @@ -535,6 +532,10 @@ /obj/item/reagent_containers/cup/glass )) +/obj/item/storage/belt/military/snack/full + +/obj/item/storage/belt/military/snack/full/Initialize(mapload) + . = ..() var/amount = 5 var/rig_snacks while(contents.len <= amount) diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index c0eec104f91..8b519550fc4 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -607,6 +607,15 @@ group.register(i) desc += " The implants are registered to the \"[group.name]\" group." +/obj/item/storage/box/syndie_kit/pinata + name = "weapons grade pinata kit" + desc = "Contains a weapons grade pinata and 2 belts for carrying its contents." + +/obj/item/storage/box/syndie_kit/pinata/PopulateContents() + new /obj/item/pinata/syndie(src) + new /obj/item/storage/belt/grenade(src) + new /obj/item/storage/belt/military/snack(src) + #undef KIT_RECON #undef KIT_BLOODY_SPAI #undef KIT_STEALTHY diff --git a/code/game/objects/structures/pinatas.dm b/code/game/objects/structures/pinatas.dm new file mode 100644 index 00000000000..116de95c167 --- /dev/null +++ b/code/game/objects/structures/pinatas.dm @@ -0,0 +1,92 @@ +///A pinata that has a chance to drop candy items when struck with a melee weapon that deals at least 10 damage +/obj/structure/pinata + name = "corgi pinata" + desc = "A papier-mâché representation of a corgi that contains all sorts of sugary treats." + icon = 'icons/obj/toys/toy.dmi' + icon_state = "pinata_placed" + base_icon_state = "pinata_placed" + max_integrity = 300 //20 hits from a baseball bat + anchored = TRUE + ///What sort of candy the pinata will contain + var/candy_options = list( + /obj/item/food/bubblegum, + /obj/item/food/candy, + /obj/item/food/chocolatebar, + /obj/item/food/gumball, + /obj/item/food/lollipop/cyborg, + ) + ///How much candy is dropped when the pinata is destroyed + var/destruction_loot = 5 + ///Debris dropped when the pinata is destroyed + var/debris = /obj/effect/decal/cleanable/wrapping/pinata + +/obj/structure/pinata/Initialize(mapload) + . = ..() + AddComponent(/datum/component/pinata, candy = candy_options, death_drop = destruction_loot) + +/obj/structure/pinata/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration) + . = ..() + if(get_integrity() < (max_integrity/2)) + icon_state = "[base_icon_state]_damaged" + if(damage_amount >= 10) // Swing means minimum damage threshhold for dropping candy is met. + flick("[icon_state]_swing", src) + +/obj/structure/pinata/play_attack_sound(damage_amount, damage_type, damage_flag) + switch(damage_type) + if(BRUTE) + if(damage_amount) + playsound(src, 'sound/weapons/slash.ogg', 50, TRUE) + else + playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) + if(BURN) + playsound(src, 'sound/items/welder.ogg', 100, TRUE) + +/obj/structure/pinata/deconstruct(disassembled) + new debris(get_turf(src)) + return ..() + +///An item that when used inhand spawns an immovable pinata +/obj/item/pinata + name = "pinata assembly kit" + desc = "A papier-mâché corgi that contains various candy, must be set up before you can smash it." + icon = 'icons/obj/toys/toy.dmi' + icon_state = "pinata" + ///The pinata that is created when this is placed. + var/pinata_type = /obj/structure/pinata + +/obj/item/pinata/attack_self(mob/user) + var/turf/player_turf = get_turf(user) + if(player_turf?.is_blocked_turf(TRUE)) + return FALSE + balloon_alert_to_viewers("setting up pinata...") + if(!do_after(user, 4 SECONDS, target = get_turf(user), progress = TRUE)) + balloon_alert(user, "cancelled!") + new pinata_type(get_turf(user)) + balloon_alert(user, "pinata setup") + qdel(src) + +/obj/structure/pinata/syndie + name = "syndicate corgi pinata" + desc = "A papier-mâché representation of a corgi that contains all sorts of bombastic treats." + icon_state = "pinata_syndie_placed" + base_icon_state = "pinata_syndie_placed" + destruction_loot = 2 + debris = /obj/effect/decal/cleanable/wrapping/pinata/syndie + candy_options = list( + /obj/item/food/bubblegum, + /obj/item/food/candy, + /obj/item/food/chocolatebar, + /obj/item/food/gumball, + /obj/item/food/lollipop, + /obj/item/grenade/c4, + /obj/item/grenade/clusterbuster/soap, + /obj/item/grenade/empgrenade, + /obj/item/grenade/frag, + /obj/item/grenade/syndieminibomb, + ) //Candy items at the top, explosives at the bottom to be easier to read instead of fully alphabetized. + +/obj/item/pinata/syndie + name = "weapons grade pinata assembly kit" + desc = "A papier-mâché corgi that contains various candy and explosives, must be set up before you can smash it." + icon_state = "pinata_syndie" + pinata_type = /obj/structure/pinata/syndie diff --git a/code/modules/cargo/packs/costumes_toys.dm b/code/modules/cargo/packs/costumes_toys.dm index b892dfea0dc..e50a1305ff3 100644 --- a/code/modules/cargo/packs/costumes_toys.dm +++ b/code/modules/cargo/packs/costumes_toys.dm @@ -293,3 +293,15 @@ for(var/i in 1 to 10) cardpacktype = pick(subtypesof(/obj/item/cardpack)) new cardpacktype(C) + +/datum/supply_pack/constumes_toys/pinata + name = "Corgi Pinata Kit" + desc = "This crate contains a pinata full of candy, a blindfold and a bat for smashing it." + cost = CARGO_CRATE_VALUE * 4 + contains = list( + /obj/item/pinata, + /obj/item/melee/baseball_bat, + /obj/item/clothing/glasses/blindfold, + ) + crate_name = "corgi pinata kit" + crate_type = /obj/structure/closet/crate/wooden diff --git a/code/modules/uplink/uplink_items/nukeops.dm b/code/modules/uplink/uplink_items/nukeops.dm index c87e6d11bc8..d56896b31fc 100644 --- a/code/modules/uplink/uplink_items/nukeops.dm +++ b/code/modules/uplink/uplink_items/nukeops.dm @@ -497,6 +497,15 @@ surplus = 35 purchasable_from = UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS +/datum/uplink_item/explosives/pinata + name = "Weapons Grade Pinata Kit" + desc = "A pinata filled with both candy and explosives as well as two belts to carry them on, crack it open and see what you get!" + item = /obj/item/storage/box/syndie_kit/pinata + purchasable_from = UPLINK_CLOWN_OPS + limited_stock = 1 + cost = 12 //This is effectively the clown ops version of the grenadier belt where you should on average get 8 explosives if you use a weapon with exactly 10 force. + surplus = 0 + //Support and Mechs /datum/uplink_category/support name = "Support and Exosuits" @@ -812,3 +821,4 @@ item = /obj/item/keycard/syndicate_fridge cost = 5 purchasable_from = UPLINK_CLOWN_OPS | UPLINK_NUKE_OPS + diff --git a/icons/obj/objects.dmi b/icons/obj/objects.dmi index 6fb3cdf9f26..1770f9f92a7 100644 Binary files a/icons/obj/objects.dmi and b/icons/obj/objects.dmi differ diff --git a/icons/obj/toys/toy.dmi b/icons/obj/toys/toy.dmi index 69d5705d2af..1d94bb1b693 100644 Binary files a/icons/obj/toys/toy.dmi and b/icons/obj/toys/toy.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 84a017737d5..953a5848b6c 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1007,6 +1007,7 @@ #include "code\datums\components\payment.dm" #include "code\datums\components\pellet_cloud.dm" #include "code\datums\components\phylactery.dm" +#include "code\datums\components\pinata.dm" #include "code\datums\components\pricetag.dm" #include "code\datums\components\pry_open_door.dm" #include "code\datums\components\punchcooldown.dm" @@ -2173,6 +2174,7 @@ #include "code\game\objects\structures\mystery_box.dm" #include "code\game\objects\structures\noticeboard.dm" #include "code\game\objects\structures\petrified_statue.dm" +#include "code\game\objects\structures\pinatas.dm" #include "code\game\objects\structures\plasticflaps.dm" #include "code\game\objects\structures\railings.dm" #include "code\game\objects\structures\reflector.dm"