diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm index cf8d38d9c9..bd5e959a3f 100644 --- a/code/game/objects/items/cigs_lighters.dm +++ b/code/game/objects/items/cigs_lighters.dm @@ -863,3 +863,201 @@ CIGARETTE PACKETS ARE IN FANCY.DM if(reagents && reagents.total_volume) hand_reagents() + +/////////////// +/////BONGS///// +/////////////// + +/obj/item/bong + name = "bong" + desc = "A water bong used for smoking dried plants." + icon = 'icons/obj/bongs.dmi' + icon_state = null + item_state = null + w_class = WEIGHT_CLASS_NORMAL + light_color = "#FFCC66" + var/icon_off = "bong" + var/icon_on = "bong_lit" + var/chem_volume = 100 + var/last_used_time //for cooldown + var/firecharges = 0 //used for counting how many hits can be taken before the flame goes out + var/list/list_reagents = list() //For the base reagents bongs could get + + +/obj/item/bong/Initialize() + . = ..() + create_reagents(chem_volume, NO_REACT) // so it doesn't react until you light it + reagents.add_reagent_list(list_reagents) + icon_state = icon_off + +/obj/item/bong/attackby(obj/item/O, mob/user, params) + . = ..() + //If we're using a dried plant.. + if(istype(O,/obj/item/reagent_containers/food/snacks)) + var/obj/item/reagent_containers/food/snacks/DP = O + if (DP.dry) + //Nothing if our bong is full + if (reagents.holder_full()) + user.show_message("The bowl is full!", 1) + return + + //Transfer reagents and remove the plant + user.show_message("You stuff the [DP] into the [src]'s bowl.", 1) + DP.reagents.trans_to(src, 100) + qdel(DP) + return + else + user.show_message("[DP] must be dried first!", 1) + return + + if (O.heat > 500) + if (reagents && reagents.total_volume) //if there's stuff in the bong + var/lighting_text = O.ignition_effect(src, user) + if(lighting_text) + //Logic regarding igniting it on + if (firecharges == 0) + user.show_message("You light the [src] with the [O]!", 1) + bongturnon() + else + user.show_message("You rekindle [src]'s flame with the [O]!", 1) + + firecharges = 1 + return + else + user.show_message("There's nothing to light up in the bowl.", 1) + return + +/obj/item/bong/CtrlShiftClick(mob/user) //empty reagents on alt click + if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user))) + return + + if (reagents && reagents.total_volume) + user.show_message("You empty the [src].", 1) + reagents.clear_reagents() + if(firecharges) + firecharges = 0 + bongturnoff() + else + user.show_message("The [src] is already empty.", 1) + +/obj/item/bong/AltClick(mob/user) + if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user))) + return + + if(firecharges) + firecharges = 0 + bongturnoff() + user.show_message("You quench the flame.", 1) + +/obj/item/bong/examine(mob/user) + ..() + if(!reagents.total_volume) + to_chat(user, "The bowl is empty.") + else if (reagents.total_volume > 80) + to_chat(user, "The bowl is filled to the brim.") + else if (reagents.total_volume > 40) + to_chat(user, "The bowl has plenty weed in it.") + else + to_chat(user, "The bowl has some weed in it.") + + to_chat(user, "Ctrl+Shift-click to empty.") + to_chat(user, "Alt-click to extinguish.") + +/obj/item/bong/ignition_effect(atom/A, mob/user) + if(firecharges) + . = "[user] lights [A] off of the [src]." + else + . = "" + +/obj/item/bong/attack(mob/living/carbon/M, mob/living/carbon/user, obj/target) + //if it's lit up, some stuff in the bowl and the user is a target, and we're not on cooldown + if (!reagents) + return + + if (M ==! user) + return + + if(user.is_mouth_covered(head_only = 1)) + to_chat(user, "Remove your headgear first.") + return + + if(user.is_mouth_covered(mask_only = 1)) + to_chat(user, "Remove your mask first.") + return + + if (!reagents.total_volume) + to_chat(user, "There's nothing in the bowl.") + return + + if (!firecharges) + to_chat(user, "You have to light it up first.") + return + + if (last_used_time + 30 < world.time) + var/hit_strength + var/noise + var/hittext = "" + //if the intent is help then you take a small hit, else a big one + if (user.a_intent == INTENT_HARM) + hit_strength = 2 + noise = 100 + hittext = "big hit" + else + hit_strength = 1 + noise = 70 + hittext = "hit" + //bubbling sound + playsound(user.loc,'sound/effects/bonghit.ogg', noise, 1) + + last_used_time = world.time + + //message + user.visible_message("[user] begins to take a "+ hittext +" from the [src]!", \ + "You begin to take a "+ hittext +" from [src].") + + //we take a hit here, after an uninterrupted delay + if(do_after(user, 25, target = user)) + if (reagents && reagents.total_volume) + var/fraction = 12 * hit_strength + + var/datum/effect_system/smoke_spread/chem/smoke_machine/s = new + s.set_up(reagents, hit_strength, 18, user.loc) + s.start() + + reagents.reaction(user, INGEST, fraction) + if(!reagents.trans_to(user, fraction)) + reagents.remove_any(fraction) + + if (hit_strength == 2 && prob(15)) + user.emote("cough") + user.adjustOxyLoss(15) + + //playsound(user.loc, 'sound/effects/smoke.ogg', 10, 1, -3) + + user.visible_message("[user] takes a "+ hittext +" from the [src]!", \ + "You take a "+ hittext +" from [src].") + + firecharges = firecharges - 1 + if (firecharges == 0) + bongturnoff() + if (!reagents.total_volume) + firecharges = 0 + bongturnoff() + + + +/obj/item/bong/proc/bongturnon() + icon_state = icon_on + set_light(3, 0.8) + +/obj/item/bong/proc/bongturnoff() + icon_state = icon_off + set_light(0, 0.0) + + + +/obj/item/bong/coconut + name = "coconut bong" + icon_off = "coconut_bong" + icon_on = "coconut_bong_lit" + desc = "A water bong used for smoking dried plants. This one's made out of a coconut and some bamboo" \ No newline at end of file diff --git a/code/modules/crafting/recipes/recipes_misc.dm b/code/modules/crafting/recipes/recipes_misc.dm index 70dd4d1370..38ae3560e5 100644 --- a/code/modules/crafting/recipes/recipes_misc.dm +++ b/code/modules/crafting/recipes/recipes_misc.dm @@ -358,3 +358,11 @@ time = 100 category = CAT_MISC always_availible = FALSE // Disabled til learned + +/datum/crafting_recipe/coconut_bong + name = "Coconut Bong" + result = /obj/item/bong/coconut + reqs = list(/obj/item/stack/sheet/mineral/bamboo = 2, + /obj/item/reagent_containers/food/snacks/grown/coconut = 1) + time = 70 + category = CAT_MISC \ No newline at end of file diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm index 3376bac1cc..916390b488 100644 --- a/code/modules/hydroponics/gene_modder.dm +++ b/code/modules/hydroponics/gene_modder.dm @@ -240,7 +240,7 @@ dat += "" else dat += "No trait-related genes detected in sample.
" - if(can_insert && istype(disk.gene, /datum/plant_gene/trait)) + if(can_insert && istype(disk.gene, /datum/plant_gene/trait) && !seed.is_gene_forbidden(disk.gene)) dat += "Insert: [disk.gene.get_name()]" dat += "" else diff --git a/code/modules/hydroponics/grown/misc.dm b/code/modules/hydroponics/grown/misc.dm index b37e494233..dbeddfdbd0 100644 --- a/code/modules/hydroponics/grown/misc.dm +++ b/code/modules/hydroponics/grown/misc.dm @@ -232,7 +232,7 @@ /obj/item/reagent_containers/food/snacks/grown/cherry_bomb/proc/detonate() reagents.chem_temp = 1000 //Sets off the black powder reagents.handle_reactions() - + // Lavaland cactus /obj/item/seeds/lavaland/cactus @@ -244,3 +244,283 @@ product = /obj/item/reagent_containers/food/snacks/grown/ash_flora/cactus_fruit growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' growthstages = 2 + + +// Coconut +/obj/item/seeds/coconut + name = "pack of coconut seeds" + desc = "They're seeds that grow into coconut palm trees." + icon_state = "seed-coconut" + species = "coconut" + plantname = "Coconut Palm Tree" + product = /obj/item/reagent_containers/food/snacks/grown/coconut + lifespan = 50 + endurance = 30 + potency = 35 + growing_icon = 'icons/obj/hydroponics/growing.dmi' + icon_dead = "coconut-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + forbiddengenes = list(/datum/plant_gene/trait/squash, /datum/plant_gene/trait/stinging) + reagents_add = list("coconutmilk" = 0.3) + +/obj/item/reagent_containers/food/snacks/grown/coconut + seed = /obj/item/seeds/coconut + name = "coconut" + desc = "Hard shell of a nut containing delicious milk inside. Perhaps try using something sharp?" + icon_state = "coconut" + item_state = "coconut" + possible_transfer_amounts = list(5, 10, 15, 20, 25, 30, 50) + spillable = FALSE + resistance_flags = ACID_PROOF + volume = 150 //so it won't cut reagents despite having the capacity for them + w_class = WEIGHT_CLASS_SMALL + force = 5 + throwforce = 5 + hitsound = 'sound/weapons/klonk.ogg' + attack_verb = list("klonked", "donked", "bonked") + var/opened = FALSE + var/carved = FALSE + var/chopped = FALSE + var/straw = FALSE + var/fused = FALSE + var/fusedactive = FALSE + var/defused = FALSE + +/obj/item/reagent_containers/food/snacks/grown/coconut/Initialize(mapload, obj/item/seeds/new_seed) + . = ..() + var/newvolume + newvolume = 50 + round(seed.potency,10) + if (seed.get_gene(/datum/plant_gene/trait/maxchem)) + newvolume = newvolume + 50 + volume = newvolume + reagents.maximum_volume = newvolume + reagents.update_total() + + transform *= TRANSFORM_USING_VARIABLE(40, 100) + 0.5 //temporary fix for size? + +/obj/item/reagent_containers/food/snacks/grown/coconut/attack_self(mob/user) + if (opened == TRUE) + if(possible_transfer_amounts.len) + var/i=0 + for(var/A in possible_transfer_amounts) + i++ + if(A == amount_per_transfer_from_this) + if(i[src]'s transfer amount is now [amount_per_transfer_from_this] units.") + return + +/obj/item/reagent_containers/food/snacks/grown/coconut/attackby(obj/item/W, mob/user, params) + //DEFUSING NADE LOGIC + if (W.tool_behaviour == TOOL_WIRECUTTER && fused == TRUE) + user.show_message("You cut the fuse!", 1) + playsound(user, W.hitsound, 50, 1, -1) + icon_state = "coconut_carved" + desc = "A coconut. This one's got a hole in it." + name = "coconut" + defused = TRUE + fused = FALSE + fusedactive = FALSE + if(!seed.get_gene(/datum/plant_gene/trait/glow)) + set_light(0, 0.0) + return + //IGNITING NADE LOGIC + if(fusedactive == FALSE && fused == TRUE) + var/lighting_text = W.ignition_effect(src, user) + if(lighting_text) + user.visible_message("[user] ignites [src]'s fuse!", "You ignite the [src]'s fuse!") + fusedactive = TRUE + defused = FALSE + playsound(src, 'sound/effects/fuse.ogg', 100, 0) + message_admins("[ADMIN_LOOKUPFLW(user)] ignited a coconut bomb for detonation at [ADMIN_VERBOSEJMP(user)] "+ pretty_string_from_reagent_list(reagents.reagent_list)) + log_game("[key_name(user)] primed a coconut grenade for detonation at [AREACOORD(user)].") + addtimer(CALLBACK(src, .proc/prime), 5 SECONDS) + icon_state = "coconut_grenade_active" + desc = "RUN!" + if(!seed.get_gene(/datum/plant_gene/trait/glow)) + light_color = "#FFCC66" //for the fuse + set_light(3, 0.8) + return + + //ADDING A FUSE, NADE LOGIC + if (istype(W,/obj/item/stack/sheet/cloth) || istype(W,/obj/item/stack/sheet/durathread)) + if (carved == TRUE && straw == FALSE && fused == FALSE) + user.show_message("You add a fuse to the coconut!", 1) + W.use(1) + fused = TRUE + icon_state = "coconut_grenade" + desc = "A makeshift bomb made out of a coconut. You estimate the fuse is long enough for 5 seconds." + name = "coconut bomb" + return + //ADDING STRAW LOGIC + if (istype(W,/obj/item/stack/sheet/mineral/bamboo) && opened == TRUE && straw == FALSE && fused == FALSE) + user.show_message("You add a bamboo straw to the coconut!", 1) + straw = TRUE + W.use(1) + icon_state += "_straw" + desc = "You can already feel like you're on a tropical vacation." + //OPENING THE NUT LOGIC + if (carved == FALSE && chopped == FALSE) + if(W.tool_behaviour == TOOL_SCREWDRIVER) + user.show_message("You make a hole in the coconut!", 1) + carved = TRUE + opened = TRUE + reagent_flags = OPENCONTAINER + ENABLE_BITFIELD(reagents.reagents_holder_flags, OPENCONTAINER) + icon_state = "coconut_carved" + desc = "A coconut. This one's got a hole in it." + playsound(user, W.hitsound, 50, 1, -1) + return + else if(W.sharpness) + user.show_message("You slice the coconut open!", 1) + chopped = TRUE + opened = TRUE + reagent_flags = OPENCONTAINER + ENABLE_BITFIELD(reagents.reagents_holder_flags, OPENCONTAINER) + spillable = TRUE + icon_state = "coconut_chopped" + desc = "A coconut. This one's sliced open, with all its delicious contents for your eyes to savour." + playsound(user, W.hitsound, 50, 1, -1) + return + +/obj/item/reagent_containers/food/snacks/grown/coconut/attack(mob/living/M, mob/user, obj/target) + if(M && user.a_intent == INTENT_HARM && spillable == FALSE) + var/obj/item/bodypart/affecting = user.zone_selected //Find what the player is aiming at + if (affecting == BODY_ZONE_HEAD && prob(15)) + //smash the nut open + var/armor_block = min(90, M.run_armor_check(affecting, "melee", null, null,armour_penetration)) // For normal attack damage + M.apply_damage(force, BRUTE, affecting, armor_block) + + //Sound + playsound(user, hitsound, 100, 1, -1) + + //Attack logs + log_combat(user, M, "attacked", src) + + //Display an attack message. + if(M != user) + M.visible_message("[user] has cracked open a [src.name] on [M]'s head!", \ + "[user] has cracked open a [src.name] on [M]'s head!") + else + user.visible_message("[M] cracks open a [src.name] on their [M.p_them()] head!", \ + "[M] cracks open a [src.name] on [M.p_their()] head!") + + //The coconut breaks open so splash its reagents + spillable = TRUE + SplashReagents(M) + + //Lastly we remove the nut + qdel(src) + else + . = ..() + return + + if(fusedactive) + return + + if(opened == FALSE) + return + + if(!canconsume(M, user)) + return + + if(!reagents || !reagents.total_volume) + to_chat(user, "[src] is empty!") + return + + if(istype(M)) + if(user.a_intent == INTENT_HARM && spillable == TRUE) + var/R + M.visible_message("[user] splashes the contents of [src] onto [M]!", \ + "[user] splashes the contents of [src] onto [M]!") + if(reagents) + for(var/datum/reagent/A in reagents.reagent_list) + R += A.id + " (" + R += num2text(A.volume) + ")," + if(isturf(target) && reagents.reagent_list.len && thrownby) + log_combat(thrownby, target, "splashed (thrown) [english_list(reagents.reagent_list)]") + message_admins("[ADMIN_LOOKUPFLW(thrownby)] splashed (thrown) [english_list(reagents.reagent_list)] on [target] at [ADMIN_VERBOSEJMP(target)].") + reagents.reaction(M, TOUCH) + log_combat(user, M, "splashed", R) + reagents.clear_reagents() + else + if(M != user) + M.visible_message("[user] attempts to feed something to [M].", \ + "[user] attempts to feed something to you.") + if(!do_mob(user, M)) + return + if(!reagents || !reagents.total_volume) + return // The drink might be empty after the delay, such as by spam-feeding + M.visible_message("[user] feeds something to [M].", "[user] feeds something to you.") + log_combat(user, M, "fed", reagents.log_list()) + else + to_chat(user, "You swallow a gulp of [src].") + var/fraction = min(5/reagents.total_volume, 1) + reagents.reaction(M, INGEST, fraction) + addtimer(CALLBACK(reagents, /datum/reagents.proc/trans_to, M, 5), 5) + playsound(M.loc,'sound/items/drink.ogg', rand(10,50), 1) + +/obj/item/reagent_containers/food/snacks/grown/coconut/afterattack(obj/target, mob/user, proximity) + . = ..() + if(fusedactive) + return + + if((!proximity) || !check_allowed_items(target,target_self=1)) + return + + if(target.is_refillable()) //Something like a glass. Player probably wants to transfer TO it. + if(!reagents.total_volume) + to_chat(user, "[src] is empty!") + return + + if(target.reagents.holder_full()) + to_chat(user, "[target] is full.") + return + + var/trans = reagents.trans_to(target, amount_per_transfer_from_this) + to_chat(user, "You transfer [trans] unit\s of the solution to [target].") + + else if(target.is_drainable()) //A dispenser. Transfer FROM it TO us. + if(!target.reagents.total_volume) + to_chat(user, "[target] is empty and can't be refilled!") + return + + if(reagents.holder_full()) + to_chat(user, "[src] is full.") + return + + var/trans = target.reagents.trans_to(src, amount_per_transfer_from_this) + to_chat(user, "You fill [src] with [trans] unit\s of the contents of [target].") + + else if(reagents.total_volume) + if(user.a_intent == INTENT_HARM && spillable == TRUE) + user.visible_message("[user] splashes the contents of [src] onto [target]!", \ + "You splash the contents of [src] onto [target].") + reagents.reaction(target, TOUCH) + reagents.clear_reagents() + +/obj/item/reagent_containers/food/snacks/grown/coconut/dropped(mob/user) + . = ..() + transform *= TRANSFORM_USING_VARIABLE(40, 100) + 0.5 //temporary fix for size? + +/obj/item/reagent_containers/food/snacks/grown/coconut/proc/prime() + if (!defused) + var/turf/T = get_turf(src) + reagents.chem_temp = 1000 + //Disable seperated contents when the grenade primes + if (seed.get_gene(/datum/plant_gene/trait/noreact)) + DISABLE_BITFIELD(reagents.reagents_holder_flags, NO_REACT) + reagents.handle_reactions() + log_game("Coconut bomb detonation at [AREACOORD(T)], location [loc]") + qdel(src) + +/obj/item/reagent_containers/food/snacks/grown/coconut/ex_act(severity) + qdel(src) + +/obj/item/reagent_containers/food/snacks/grown/coconut/deconstruct(disassembled = TRUE) + if(!disassembled && fused) + prime() + if(!QDELETED(src)) + qdel(src) \ No newline at end of file diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm index 4132719389..904bc5228c 100644 --- a/code/modules/hydroponics/seeds.dm +++ b/code/modules/hydroponics/seeds.dm @@ -27,6 +27,7 @@ var/rarity = 0 // How rare the plant is. Used for giving points to cargo when shipping off to CentCom. var/list/mutatelist = list() // The type of plants that this plant can mutate into. var/list/genes = list() // Plant genes are stored here, see plant_genes.dm for more info. + var/list/forbiddengenes = list() var/list/reagents_add = list() // A list of reagents to add to product. // Format: "reagent_id" = potency multiplier @@ -96,6 +97,10 @@ S.reagents_add = reagents_add.Copy() // Faster than grabbing the list from genes. return S +obj/item/seeds/proc/is_gene_forbidden(typepath) + return (locate(typepath) in forbiddengenes) + + /obj/item/seeds/proc/get_gene(typepath) return (locate(typepath) in genes) @@ -448,7 +453,7 @@ for(var/i in 1 to amount_random_traits) var/random_trait = pick((subtypesof(/datum/plant_gene/trait)-typesof(/datum/plant_gene/trait/plant_type))) var/datum/plant_gene/trait/T = new random_trait - if(T.can_add(src)) + if(T.can_add(src) && !is_gene_forbidden(T)) genes += T else qdel(T) diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm index 5d31bfae9b..e8551074e7 100644 --- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm @@ -247,6 +247,23 @@ . = 1 ..() +/datum/reagent/consumable/coconutmilk + name = "Coconut Milk" + id = "coconutmilk" + description = "A transparent white liquid extracted from coconuts. Rich in taste." + color = "#DFDFDF" // rgb: 223, 223, 223 + taste_description = "sweet milk" + quality = DRINK_GOOD + glass_icon_state = "glass_white" + glass_name = "glass of coconut milk" + glass_desc = "White and nutritious goodness!" + +/datum/reagent/consumable/coconutmilk/on_mob_life(mob/living/carbon/M) + if(M.getBruteLoss() && prob(20)) + M.heal_bodypart_damage(2,0, 0) + . = 1 + ..() + /datum/reagent/consumable/cream name = "Cream" id = "cream" diff --git a/code/modules/vending/megaseed.dm b/code/modules/vending/megaseed.dm index 5c092c3659..03241c4a86 100644 --- a/code/modules/vending/megaseed.dm +++ b/code/modules/vending/megaseed.dm @@ -14,6 +14,7 @@ /obj/item/seeds/chanter = 3, /obj/item/seeds/chili = 3, /obj/item/seeds/cocoapod = 3, + /obj/item/seeds/coconut = 3, /obj/item/seeds/coffee = 3, /obj/item/seeds/cotton = 3, /obj/item/seeds/corn = 3, diff --git a/icons/obj/bongs.dmi b/icons/obj/bongs.dmi new file mode 100644 index 0000000000..406cce3817 Binary files /dev/null and b/icons/obj/bongs.dmi differ diff --git a/icons/obj/hydroponics/growing.dmi b/icons/obj/hydroponics/growing.dmi index 469b1e1aff..712ea11a3b 100644 Binary files a/icons/obj/hydroponics/growing.dmi and b/icons/obj/hydroponics/growing.dmi differ diff --git a/icons/obj/hydroponics/harvest.dmi b/icons/obj/hydroponics/harvest.dmi index a57719fb3a..e746d8f43d 100644 Binary files a/icons/obj/hydroponics/harvest.dmi and b/icons/obj/hydroponics/harvest.dmi differ diff --git a/icons/obj/hydroponics/seeds.dmi b/icons/obj/hydroponics/seeds.dmi index 8695d03b3c..43e231cbab 100644 Binary files a/icons/obj/hydroponics/seeds.dmi and b/icons/obj/hydroponics/seeds.dmi differ diff --git a/sound/effects/bonghit.ogg b/sound/effects/bonghit.ogg new file mode 100644 index 0000000000..45a0dec1b6 Binary files /dev/null and b/sound/effects/bonghit.ogg differ diff --git a/sound/weapons/klonk.ogg b/sound/weapons/klonk.ogg new file mode 100644 index 0000000000..471f3ad8b7 Binary files /dev/null and b/sound/weapons/klonk.ogg differ