diff --git a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm index 82f4d367780..85b662d04aa 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm @@ -406,6 +406,10 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4, /turf/open/floor/plating, /area/ruin/powered/beach) +"mv" = ( +/obj/effect/spawner/message_in_a_bottle/low_prob, +/turf/open/misc/beach/coast, +/area/ruin/powered/beach) "mw" = ( /obj/structure/flora/coconuts, /turf/open/misc/beach/sand, @@ -985,6 +989,7 @@ dir = 1 }, /obj/machinery/light/directional/south, +/obj/effect/spawner/message_in_a_bottle/low_prob, /turf/open/misc/beach/coast/corner{ dir = 8 }, @@ -1990,7 +1995,7 @@ bG bL VI VI -yT +mv pz Eu pz diff --git a/_maps/RandomZLevels/TheBeach.dmm b/_maps/RandomZLevels/TheBeach.dmm index 602e11e3f33..ea38a0cc477 100644 --- a/_maps/RandomZLevels/TheBeach.dmm +++ b/_maps/RandomZLevels/TheBeach.dmm @@ -92,6 +92,10 @@ /obj/structure/sign/clock/directional/north, /turf/open/floor/wood, /area/awaymission/beach) +"aY" = ( +/obj/item/broken_bottle, +/turf/open/misc/beach/sand, +/area/awaymission/beach) "bg" = ( /obj/structure/no_effect_signpost{ pixel_y = 6; @@ -174,6 +178,11 @@ /obj/machinery/light/directional/west, /turf/open/floor/carpet/cyan, /area/awaymission/beach) +"cu" = ( +/obj/effect/mob_spawn/corpse/human/old_pirate_captain, +/obj/structure/bed/maint, +/turf/open/misc/beach/sand, +/area/awaymission/beach) "cv" = ( /obj/structure/table/wood, /obj/item/food/grown/watermelon{ @@ -207,6 +216,12 @@ dir = 4 }, /area/awaymission/beach) +"cL" = ( +/obj/effect/spawner/message_in_a_bottle/low_prob, +/turf/open/misc/beach/coast{ + dir = 1 + }, +/area/awaymission/beach) "cR" = ( /turf/open/floor/iron/freezer, /area/awaymission/beach) @@ -278,16 +293,6 @@ /obj/structure/musician/piano, /turf/open/floor/wood, /area/awaymission/beach) -"dx" = ( -/obj/structure/sign/directions/dorms/north_arrow{ - pixel_y = 35 - }, -/obj/structure/sign/directions/medical/north_arrow{ - pixel_y = 29 - }, -/obj/machinery/computer/slot_machine, -/turf/open/floor/wood/large, -/area/awaymission/beach) "dL" = ( /obj/machinery/door/airlock/public/glass{ name = "Locker Room" @@ -505,10 +510,6 @@ }, /turf/closed/wall/mineral/wood, /area/awaymission/beach) -"gq" = ( -/obj/item/broken_bottle, -/turf/open/misc/beach/sand, -/area/awaymission/beach) "gw" = ( /obj/item/stack/sheet/mineral/sandstone{ pixel_x = -8 @@ -575,6 +576,10 @@ }, /turf/open/floor/wood, /area/awaymission/beach) +"hr" = ( +/obj/effect/spawner/message_in_a_bottle/low_prob, +/turf/open/misc/beach/coast, +/area/awaymission/beach) "hC" = ( /obj/structure/railing{ dir = 8 @@ -898,17 +903,6 @@ /obj/structure/table/wood, /turf/open/floor/wood/large, /area/awaymission/beach) -"lk" = ( -/obj/effect/turf_decal/siding/wood{ - dir = 9 - }, -/obj/effect/turf_decal/tile/dark/half{ - pixel_y = -1; - pixel_x = 5; - dir = 8 - }, -/turf/open/water/beach, -/area/awaymission/beach) "ll" = ( /obj/machinery/door/airlock/wood{ name = "Room 11"; @@ -1195,10 +1189,6 @@ }, /turf/open/misc/beach/sand, /area/awaymission/beach) -"pq" = ( -/obj/effect/spawner/random/trash/cigbutt, -/turf/open/misc/beach/sand, -/area/awaymission/beach) "pr" = ( /obj/item/reagent_containers/cup/soda_cans/space_mountain_wind{ pixel_x = -17; @@ -1284,6 +1274,11 @@ /obj/structure/sink/kitchen/directional/south, /turf/open/floor/wood/large, /area/awaymission/beach) +"qb" = ( +/obj/structure/closet/crate/bin, +/obj/effect/spawner/message_in_a_bottle/low_prob, +/turf/open/floor/wood, +/area/awaymission/beach) "qf" = ( /obj/structure/table/bronze, /obj/item/pai_card{ @@ -1710,6 +1705,12 @@ }, /turf/open/water/beach, /area/awaymission/beach) +"vC" = ( +/obj/effect/spawner/message_in_a_bottle/low_prob, +/turf/open/misc/beach/coast{ + dir = 4 + }, +/area/awaymission/beach) "vF" = ( /obj/structure/table/bronze, /obj/structure/sign/poster/random/directional/east, @@ -1840,6 +1841,18 @@ }, /turf/open/floor/wood/large, /area/awaymission/beach) +"xt" = ( +/obj/effect/turf_decal/siding/wood{ + dir = 9 + }, +/obj/effect/turf_decal/tile/dark/half{ + pixel_y = -1; + pixel_x = 5; + dir = 8 + }, +/obj/effect/spawner/message_in_a_bottle/low_prob, +/turf/open/water/beach, +/area/awaymission/beach) "xv" = ( /turf/closed/wall/mineral/wood, /area/awaymission/beach) @@ -2581,13 +2594,6 @@ }, /turf/open/misc/beach/sand, /area/awaymission/beach) -"FJ" = ( -/obj/item/instrument/guitar{ - pixel_y = 4; - pixel_x = -1 - }, -/turf/open/misc/beach/coast, -/area/awaymission/beach) "FK" = ( /obj/item/toy/seashell{ pixel_x = 12; @@ -2629,6 +2635,17 @@ }, /turf/open/floor/wood/parquet, /area/awaymission/beach) +"FX" = ( +/obj/item/paper/fluff/old_pirate_note{ + pixel_x = -5; + pixel_y = -7 + }, +/obj/item/pen/fountain{ + pixel_y = -11; + pixel_x = -8 + }, +/turf/open/misc/beach/sand, +/area/awaymission/beach) "FY" = ( /obj/effect/turf_decal/sand, /obj/effect/turf_decal/siding/wood/corner{ @@ -2681,6 +2698,12 @@ /obj/machinery/light/directional/north, /turf/open/floor/wood/large, /area/awaymission/beach) +"Gy" = ( +/obj/effect/spawner/message_in_a_bottle/low_prob, +/turf/open/misc/beach/coast/corner{ + dir = 4 + }, +/area/awaymission/beach) "GC" = ( /turf/open/floor/carpet, /area/awaymission/beach) @@ -2864,17 +2887,6 @@ /obj/machinery/light/directional/west, /turf/open/floor/wood/large, /area/awaymission/beach) -"Jv" = ( -/obj/item/paper/fluff/old_pirate_note{ - pixel_x = -5; - pixel_y = -7 - }, -/obj/item/pen/fountain{ - pixel_y = -11; - pixel_x = -8 - }, -/turf/open/misc/beach/sand, -/area/awaymission/beach) "JF" = ( /obj/structure/marker_beacon/burgundy, /turf/open/water/beach, @@ -3070,6 +3082,14 @@ /obj/machinery/light/directional/north, /turf/open/floor/wood, /area/awaymission/beach) +"LT" = ( +/obj/item/instrument/guitar{ + pixel_y = 4; + pixel_x = -1 + }, +/obj/effect/spawner/message_in_a_bottle/low_prob, +/turf/open/misc/beach/coast, +/area/awaymission/beach) "Mm" = ( /obj/structure/table/bronze, /turf/open/floor/wood/large, @@ -3349,12 +3369,6 @@ }, /turf/open/misc/beach/sand, /area/awaymission/beach) -"Pb" = ( -/obj/item/fishing_rod, -/obj/structure/closet/crate/trashcart, -/obj/item/reagent_containers/cup/glass/bottle/rum/aged, -/turf/open/misc/beach/sand, -/area/awaymission/beach) "Pe" = ( /turf/open/misc/beach/coast{ dir = 5 @@ -3403,11 +3417,6 @@ }, /turf/open/misc/beach/sand, /area/awaymission/beach) -"PU" = ( -/obj/effect/mob_spawn/corpse/human/old_pirate_captain, -/obj/structure/bed/maint, -/turf/open/misc/beach/sand, -/area/awaymission/beach) "Qe" = ( /obj/effect/turf_decal/sand, /obj/machinery/door/airlock/wood{ @@ -3424,6 +3433,10 @@ "Qg" = ( /turf/open/floor/wood/parquet, /area/awaymission/beach) +"Qi" = ( +/obj/effect/spawner/message_in_a_bottle/low_prob, +/turf/open/misc/beach/coast/corner, +/area/awaymission/beach) "Qk" = ( /obj/structure/closet/secure_closet/personal/cabinet, /obj/item/clothing/suit/jacket/miljacket, @@ -3550,6 +3563,12 @@ }, /turf/open/floor/wood/large, /area/awaymission/beach) +"RH" = ( +/obj/effect/spawner/message_in_a_bottle/low_prob, +/turf/open/misc/beach/coast/corner{ + dir = 8 + }, +/area/awaymission/beach) "RI" = ( /obj/machinery/door/airlock/medical/glass{ name = "Medbay Clinic" @@ -3750,6 +3769,10 @@ /obj/machinery/light/directional/east, /turf/open/floor/wood/large, /area/awaymission/beach) +"UV" = ( +/obj/effect/spawner/random/trash/cigbutt, +/turf/open/misc/beach/sand, +/area/awaymission/beach) "UX" = ( /obj/structure/flora/tree/palm/style_2{ pixel_y = 12; @@ -3897,6 +3920,10 @@ }, /turf/open/misc/beach/sand, /area/awaymission/beach) +"WD" = ( +/obj/effect/spawner/message_in_a_bottle/low_prob, +/turf/open/water/beach, +/area/awaymission/beach) "WM" = ( /obj/item/reagent_containers/cup/soda_cans/starkist{ pixel_x = -6 @@ -3911,6 +3938,12 @@ /obj/item/clothing/head/soft/black, /turf/open/floor/wood/parquet, /area/awaymission/beach) +"WU" = ( +/obj/item/fishing_rod, +/obj/structure/closet/crate/trashcart, +/obj/item/reagent_containers/cup/glass/bottle/rum/aged, +/turf/open/misc/beach/sand, +/area/awaymission/beach) "Xd" = ( /obj/structure/table/optable, /turf/open/floor/wood/large, @@ -3979,6 +4012,11 @@ }, /turf/open/misc/beach/sand, /area/awaymission/beach) +"Yp" = ( +/obj/structure/closet/crate/trashcart/filled, +/obj/effect/spawner/message_in_a_bottle/low_prob, +/turf/open/floor/wood/large, +/area/awaymission/beach) "Yq" = ( /obj/item/reagent_containers/cup/watering_can{ pixel_y = 19; @@ -4026,6 +4064,16 @@ "Zp" = ( /turf/open/floor/wood/large, /area/awaymission/beach) +"Zq" = ( +/obj/structure/sign/directions/dorms/north_arrow{ + pixel_y = 35 + }, +/obj/structure/sign/directions/medical/north_arrow{ + pixel_y = 29 + }, +/obj/machinery/computer/slot_machine, +/turf/open/floor/wood/large, +/area/awaymission/beach) "Zt" = ( /obj/structure/table/bronze, /obj/item/storage/toolbox/fishing{ @@ -7443,10 +7491,10 @@ XB XB XB XB -Vf +cL YV -Pb -gq +WU +aY hL ni XB @@ -7700,10 +7748,10 @@ XB XB XB XB -Vf +cL VY -pq -PU +UV +cu Er BK XB @@ -7959,7 +8007,7 @@ XB XB Vf VY -Jv +FX VY VY BK @@ -8217,7 +8265,7 @@ XB Vf IZ VY -pq +UV VY BK XB @@ -8473,7 +8521,7 @@ XB XB Pe Fb -tJ +RH zr VY BK @@ -11423,7 +11471,7 @@ FM LJ Gn kI -cp +qb xv Zp xv @@ -11441,7 +11489,7 @@ VP vM Zt xv -MF +Gy Wb It VY @@ -12190,7 +12238,7 @@ XB XB XB XB -XB +WD xv xv xv @@ -13240,7 +13288,7 @@ xv xv xv xv -dx +Zq Zp Zp mb @@ -13780,7 +13828,7 @@ hL Le Le Le -MF +Gy VY VY VY @@ -14760,7 +14808,7 @@ XB XB HV mi -uV +Yp xv eE un @@ -15073,7 +15121,7 @@ VY VY ls VY -BK +hr XB XB XB @@ -16314,7 +16362,7 @@ vx Bq Xd Ej -lk +xt LC XB XB @@ -17609,7 +17657,7 @@ xv xv xv xv -Fb +vC mf VY KL @@ -22783,7 +22831,7 @@ VY EC VY VY -Kh +Qi Rj XB XB @@ -25323,7 +25371,7 @@ XB XB XB MZ -MF +Gy VY xv xv @@ -25611,7 +25659,7 @@ VY VY Ff VY -BK +hr XB XB XB @@ -28665,7 +28713,7 @@ XB XB XB XB -Vf +cL wA VY xi @@ -29450,7 +29498,7 @@ VY VY Rn VY -FJ +LT xv CG Gh @@ -29696,7 +29744,7 @@ XB XB XB XB -Vf +cL VY VY IZ diff --git a/code/__DEFINES/database.dm b/code/__DEFINES/database.dm index 3d20b3b9a3c..22351b5052d 100644 --- a/code/__DEFINES/database.dm +++ b/code/__DEFINES/database.dm @@ -4,3 +4,6 @@ #define DB_QUERY_FINISHED 1 /// When there was a problem with the execution of a query. #define DB_QUERY_BROKEN 2 + +///The probability of non-maploaded photos and papers being saved as bottle messages at the end of the round. +#define MESSAGE_BOTTLE_CHANCE 0.2 diff --git a/code/__DEFINES/fish.dm b/code/__DEFINES/fish.dm index ec991b713ec..d6bcc2ec796 100644 --- a/code/__DEFINES/fish.dm +++ b/code/__DEFINES/fish.dm @@ -86,6 +86,7 @@ #define FISH_ICON_GEM "gem" #define FISH_ICON_CRAB "crab" #define FISH_ICON_JELLYFISH "jellyfish" +#define FISH_ICON_BOTTLE "bottle" #define FISH_ICON_BONE "bone" #define FISH_ICON_ELECTRIC "electric" #define FISH_ICON_WEAPON "weapon" diff --git a/code/__DEFINES/paper.dm b/code/__DEFINES/paper.dm index 0d70a2f3ca4..9cede4214bd 100644 --- a/code/__DEFINES/paper.dm +++ b/code/__DEFINES/paper.dm @@ -18,3 +18,29 @@ #define BARCODE_SCANNER_INVENTORY "inventory" #define IS_WRITING_UTENSIL(thing) (thing?.get_writing_implement_details()?["interaction_mode"] == MODE_WRITING) + +/** + * key defines used when converting a paper to and fro' a data/json list. It's really important that they stay the same + * lest we break persistence. + */ +#define LIST_PAPER_COLOR "paper_color" +#define LIST_PAPER_NAME "paper_name" + +#define LIST_PAPER_RAW_TEXT_INPUT "raw_text_input" +#define LIST_PAPER_RAW_FIELD_INPUT "raw_field_input" +#define LIST_PAPER_RAW_STAMP_INPUT "raw_stamp_input" + +#define LIST_PAPER_RAW_TEXT "raw_text" +#define LIST_PAPER_FONT "font" +#define LIST_PAPER_FIELD_COLOR "color" +#define LIST_PAPER_BOLD "bold" +#define LIST_PAPER_ADVANCED_HTML "advanced_html" + +#define LIST_PAPER_FIELD_INDEX "field_index" +#define LIST_PAPER_FIELD_DATA "field_data" +#define LIST_PAPER_IS_SIGNATURE "is_signature" + +#define LIST_PAPER_CLASS "class" +#define LIST_PAPER_STAMP_X "x" +#define LIST_PAPER_STAMP_Y "y" +#define LIST_PAPER_ROTATION "rotation" diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index eee89e7d3ca..c82b54491eb 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -233,6 +233,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_FISHING_SPOT "fishing_spot" ///Trait given to mobs that can fish without a rod #define TRAIT_PROFOUND_FISHER "profound_fisher" +/// If an atom has this trait, then you can toss a bottle with a message in it. +#define TRAIT_MESSAGE_IN_A_BOTTLE_LOCATION "message_in_a_bottle_location" /// This trait lets you evaluate someone's fitness level against your own #define TRAIT_EXAMINE_FITNESS "reveal_power_level" /// These mobs have particularly hygienic tongues diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 2cf71438e82..6bf5bba956c 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -23,6 +23,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_FOOD_SILVER" = TRAIT_FOOD_SILVER, "TRAIT_KEEP_TOGETHER" = TRAIT_KEEP_TOGETHER, "TRAIT_LIGHTING_DEBUGGED" = TRAIT_LIGHTING_DEBUGGED, + "TRAIT_MESSAGE_IN_A_BOTTLE_LOCATION" = TRAIT_MESSAGE_IN_A_BOTTLE_LOCATION, "TRAIT_RECENTLY_COINED" = TRAIT_RECENTLY_COINED, "TRAIT_RUSTY" = TRAIT_RUSTY, "TRAIT_SPINNING" = TRAIT_SPINNING, diff --git a/code/controllers/subsystem/persistence/_persistence.dm b/code/controllers/subsystem/persistence/_persistence.dm index a8c42631488..6438015d11d 100644 --- a/code/controllers/subsystem/persistence/_persistence.dm +++ b/code/controllers/subsystem/persistence/_persistence.dm @@ -48,6 +48,17 @@ SUBSYSTEM_DEF(persistence) var/tram_hits_this_round = 0 var/tram_hits_last_round = 0 + /// A json database to data/message_bottles.json + var/datum/json_database/message_bottles_database + /// An index used to create unique ids for the message bottles database + var/message_bottles_index = 0 + /** + * A list of non-maploaded photos or papers that met the 0.2% chance to be saved in the message bottles database + * because I don't want the database to feel empty unless there's someone constantly throwing bottles in the + * sea or beach/ocean fishing portals. + */ + var/list/queued_message_bottles + /datum/controller/subsystem/persistence/Initialize() load_poly() load_wall_engravings() @@ -72,11 +83,13 @@ SUBSYSTEM_DEF(persistence) save_scars() save_custom_outfits() save_delamination_counter() + save_queued_message_bottles() if(SStransport.can_fire) for(var/datum/transport_controller/linear/tram/transport as anything in SStransport.transports_by_type[TRANSPORT_TYPE_TRAM]) save_tram_history(transport.specific_transport_id) save_tram_counter() + ///Loads up Poly's speech buffer. /datum/controller/subsystem/persistence/proc/load_poly() for(var/mob/living/basic/parrot/poly/bird in GLOB.alive_mob_list) diff --git a/code/controllers/subsystem/persistence/message_bottles.dm b/code/controllers/subsystem/persistence/message_bottles.dm new file mode 100644 index 00000000000..ce1efe5e59c --- /dev/null +++ b/code/controllers/subsystem/persistence/message_bottles.dm @@ -0,0 +1,54 @@ +///This proc is used to save photos, papers and cash stored inside a bottle when tossed into the ocean. +/datum/controller/subsystem/persistence/proc/save_message_bottle(obj/item/message, bottle_type = /obj/item/reagent_containers/cup/glass/bottle) + if(isnull(message_bottles_database)) + message_bottles_database = new("data/message_bottles.json") + + var/list/data = list() + data["bottle_type"] = text2path(bottle_type) + if(istype(message, /obj/item/paper)) + var/obj/item/paper/paper = message + if(!length(paper.raw_text_inputs) && !length(paper.raw_stamp_data) && !length(paper.raw_field_input_data)) + return + data["paper"] = paper.convert_to_data() + else if(istype(message, /obj/item/photo)) + var/obj/item/photo/photo = message + if(!photo.picture?.id) + return + data["photo_id"] = photo.picture.id + else if(istype(message, /obj/item/stack/spacecash)) + var/obj/item/stack/spacecash/cash = message + data["cash"] = text2path(cash.type) + data["amount"] = cash.amount + message_bottles_index++ + message_bottles_database.set_key("message-[GLOB.round_id]-[message_bottles_index]", data) + +/datum/controller/subsystem/persistence/proc/load_message_bottle(atom/loc) + if(isnull(message_bottles_database)) + message_bottles_database = new("data/message_bottles.json") + + var/list/data = message_bottles_database.pick_and_take_key() + if(!data) + var/obj/item/reagent_containers/cup/glass/bottle/bottle = new(loc) + return bottle + + var/bottle_type = text2path(data["bottle_type"]) || /obj/item/reagent_containers/cup/glass/bottle + var/obj/item/reagent_containers/cup/glass/bottle/bottle = new bottle_type(loc) + bottle.reagents.remove_all(bottle.reagents.maximum_volume) + if(data["photo_id"]) + var/obj/item/photo/old/photo = load_photo_from_disk(data["photo_id"], bottle) + bottle.message_in_a_bottle = photo + else if(data["cash"]) + var/cash_type = text2path(data["cash"]) || /obj/item/stack/spacecash/c10 + var/obj/item/stack/spacecash/cash = new cash_type(bottle, data["amount"]) + bottle.message_in_a_bottle = cash + else if(data["paper"]) + var/obj/item/paper/paper = new(bottle) + paper.write_from_data(data["paper"]) + bottle.message_in_a_bottle = paper + + bottle.update_icon(UPDATE_OVERLAYS) + +/datum/controller/subsystem/persistence/proc/save_queued_message_bottles() + for(var/item in queued_message_bottles) + save_message_bottle(item) + queued_message_bottles = null diff --git a/code/datums/components/fishing_spot.dm b/code/datums/components/fishing_spot.dm index 6638c822ff6..cc21f22eb4b 100644 --- a/code/datums/components/fishing_spot.dm +++ b/code/datums/components/fishing_spot.dm @@ -12,7 +12,7 @@ fish_source = configuration else return COMPONENT_INCOMPATIBLE - fish_source.on_fishing_spot_init() + fish_source.on_fishing_spot_init(src) RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, PROC_REF(handle_attackby)) RegisterSignal(parent, COMSIG_FISHING_ROD_CAST, PROC_REF(handle_cast)) RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examined)) @@ -22,6 +22,7 @@ ADD_TRAIT(parent, TRAIT_FISHING_SPOT, REF(src)) /datum/component/fishing_spot/Destroy() + fish_source.on_fishing_spot_del(src) fish_source = null return ..() diff --git a/code/datums/json_database.dm b/code/datums/json_database.dm index ea3ff354b48..6baeb44ab09 100644 --- a/code/datums/json_database.dm +++ b/code/datums/json_database.dm @@ -61,6 +61,16 @@ /datum/json_database/proc/get_key(key) return cached_data[key] +/// Picks the data of a random key and then removes that key from the database. +/// Since the list is no longer inside the database, you can mutate and use it as you like. +/datum/json_database/proc/pick_and_take_key() + if(!length(cached_data)) + return null + var/key = pick(cached_data) + . = cached_data[key] + cached_data -= key + queue_save() + /// Sets the data at the key to the value, and queues a save. /datum/json_database/proc/set_key(key, value) cached_data[key] = value diff --git a/code/game/objects/effects/spawners/message_in_a_bottle.dm b/code/game/objects/effects/spawners/message_in_a_bottle.dm new file mode 100644 index 00000000000..c4ac63ad0be --- /dev/null +++ b/code/game/objects/effects/spawners/message_in_a_bottle.dm @@ -0,0 +1,25 @@ +/obj/effect/spawner/message_in_a_bottle + name = "message in a bottle" + desc = "Sending out an SOS" + icon = 'icons/effects/random_spawners.dmi' + icon_state = "message_bottle" + var/probability = 100 + +/obj/effect/spawner/message_in_a_bottle/Initialize(mapload) + . = ..() + if(!prob(probability)) + return INITIALIZE_HINT_QDEL + if(!SSpersistence.initialized) + RegisterSignal(SSpersistence, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(on_persistence_init)) + else + SSpersistence.load_message_bottle(loc) + return INITIALIZE_HINT_QDEL + +/obj/effect/spawner/message_in_a_bottle/proc/on_persistence_init(datum/source) + SIGNAL_HANDLER + UnregisterSignal(SSpersistence, COMSIG_SUBSYSTEM_POST_INITIALIZE) + SSpersistence.load_message_bottle(loc) + qdel(src) + +/obj/effect/spawner/message_in_a_bottle/low_prob + probability = 1.5 diff --git a/code/game/objects/items/food/misc.dm b/code/game/objects/items/food/misc.dm index 26852066bae..53781e8a1ea 100644 --- a/code/game/objects/items/food/misc.dm +++ b/code/game/objects/items/food/misc.dm @@ -292,6 +292,14 @@ foodtypes = FRUIT | ALCOHOL crafting_complexity = FOOD_COMPLEXITY_2 +/obj/item/food/melonkeg/CheckParts(list/parts_list) + . = ..() + var/obj/item/reagent_containers/cup/glass/bottle/bottle = locate() in contents + if(!bottle) + return + if(bottle.message_in_a_bottle) + bottle.message_in_a_bottle.forceMove(drop_location()) + /obj/item/food/honeybar name = "honey nut bar" desc = "Oats and nuts compressed together into a bar, held together with a honey glaze." diff --git a/code/game/turfs/open/sand.dm b/code/game/turfs/open/sand.dm index fb10d297e18..254c595fcbd 100644 --- a/code/game/turfs/open/sand.dm +++ b/code/game/turfs/open/sand.dm @@ -41,6 +41,10 @@ clawfootstep = FOOTSTEP_WATER heavyfootstep = FOOTSTEP_WATER +/turf/open/misc/beach/coast/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_MESSAGE_IN_A_BOTTLE_LOCATION, INNATE_TRAIT) + /turf/open/misc/beach/coast/break_tile() . = ..() icon_state = "beach" diff --git a/code/game/turfs/open/water.dm b/code/game/turfs/open/water.dm index 20c6215209a..de895754d03 100644 --- a/code/game/turfs/open/water.dm +++ b/code/game/turfs/open/water.dm @@ -43,6 +43,10 @@ immerse_overlay_color = "#7799AA" fishing_datum = /datum/fish_source/ocean/beach +/turf/open/water/beach/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_MESSAGE_IN_A_BOTTLE_LOCATION, INNATE_TRAIT) + /turf/open/water/lavaland_atmos initial_gas_mix = LAVALAND_DEFAULT_ATMOS planetary_atmos = TRUE diff --git a/code/modules/cargo/order.dm b/code/modules/cargo/order.dm index c675352d024..a10bb3da5c0 100644 --- a/code/modules/cargo/order.dm +++ b/code/modules/cargo/order.dm @@ -10,10 +10,14 @@ #define MANIFEST_ERROR_ITEM (1 << 2) /obj/item/paper/fluff/jobs/cargo/manifest + can_become_message_in_bottle = FALSE //A lot of these are spawned each round, they'd only dilute the pool and make it boring. var/order_cost = 0 var/order_id = 0 var/errors = 0 +/obj/item/paper/requisition + can_become_message_in_bottle = FALSE //A lot of these are spawned each round, they'd only dilute the pool and make it boring. + /obj/item/paper/fluff/jobs/cargo/manifest/Initialize(mapload, id, cost, manifest_can_fail = TRUE) . = ..() order_id = id @@ -99,7 +103,7 @@ return round(cost) /datum/supply_order/proc/generateRequisition(turf/T) - var/obj/item/paper/requisition_paper = new(T) + var/obj/item/paper/requisition/requisition_paper = new(T) requisition_paper.name = "requisition form - #[id] ([pack.name])" var/requisition_text = "

[station_name()] Supply Requisition

" diff --git a/code/modules/cargo/orderconsole.dm b/code/modules/cargo/orderconsole.dm index d835c52f1e8..ae46d994381 100644 --- a/code/modules/cargo/orderconsole.dm +++ b/code/modules/cargo/orderconsole.dm @@ -329,7 +329,7 @@ else //create the paper from the SSshuttle.shopping_list if(length(SSshuttle.shopping_list)) - var/obj/item/paper/requisition_paper = new(get_turf(src)) + var/obj/item/paper/requisition/requisition_paper = new(get_turf(src)) requisition_paper.name = "requisition form - [station_time_timestamp()]" var/requisition_text = "

[station_name()] Supply Requisition

" requisition_text += "
" diff --git a/code/modules/fishing/fishing_minigame.dm b/code/modules/fishing/fishing_minigame.dm index 93a86ff82a9..5db1ee6458b 100644 --- a/code/modules/fishing/fishing_minigame.dm +++ b/code/modules/fishing/fishing_minigame.dm @@ -62,7 +62,7 @@ /// Background icon state from fishing_hud.dmi var/background = "background_default" /// Fish icon state from fishing_hud.dmi - var/fish_icon = "fish" + var/fish_icon = FISH_ICON_DEF /// Fishing line visual var/datum/beam/fishing_line @@ -164,7 +164,7 @@ completion -= MAX_FISH_COMPLETION_MALUS * (difficulty * 0.01) if(HAS_MIND_TRAIT(user, TRAIT_REVEAL_FISH)) - fish_icon = GLOB.specific_fish_icons[reward_path] || "fish" + fish_icon = GLOB.specific_fish_icons[reward_path] || FISH_ICON_DEF mover.adjust_to_difficulty() @@ -352,6 +352,8 @@ send_alert("critter!!!") if(FISH_ICON_SEED) send_alert("seed!!!") + if(FISH_ICON_BOTTLE) + send_alert("bottle!!!") else send_alert("!!!") animate(lure, pixel_y = 3, time = 5, loop = -1, flags = ANIMATION_RELATIVE) diff --git a/code/modules/fishing/sources/_fish_source.dm b/code/modules/fishing/sources/_fish_source.dm index dc9b910ad8f..5aa03ae8c0c 100644 --- a/code/modules/fishing/sources/_fish_source.dm +++ b/code/modules/fishing/sources/_fish_source.dm @@ -18,6 +18,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) /mob/living/basic/mining = FISH_ICON_HOSTILE, /obj/effect/decal/remains = FISH_ICON_BONE, /obj/effect/mob_spawn/corpse = FISH_ICON_BONE, + /obj/effect/spawner/message_in_a_bottle = FISH_ICON_BOTTLE, /obj/item/coin = FISH_ICON_COIN, /obj/item/fish = FISH_ICON_DEF, /obj/item/fish/armorfish = FISH_ICON_CRAB, @@ -46,6 +47,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) /obj/item/stack/sheet/mineral = FISH_ICON_GEM, /obj/item/stack/ore = FISH_ICON_GEM, /obj/structure/closet/crate = FISH_ICON_COIN, + /obj/structure/mystery_box = FISH_ICON_COIN, )) return_list[FISHING_RANDOM_SEED] = FISH_ICON_SEED @@ -97,9 +99,12 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) return ..() ///Called when src is set as the fish source of a fishing spot component -/datum/fish_source/proc/on_fishing_spot_init(/datum/component/fishing_spot/spot) +/datum/fish_source/proc/on_fishing_spot_init(datum/component/fishing_spot/spot) return +///Called whenever a fishing spot with this fish source attached is deleted +/datum/fish_source/proc/on_fishing_spot_del(datum/component/fishing_spot/spot) + /// Can we fish in this spot at all. Returns DENIAL_REASON or null if we're good to go /datum/fish_source/proc/reason_we_cant_fish(obj/item/fishing_rod/rod, mob/fisherman, atom/parent) return rod.reason_we_cant_fish(src) diff --git a/code/modules/fishing/sources/source_types.dm b/code/modules/fishing/sources/source_types.dm index 5b4b6fe3624..6a7c8d3e1ef 100644 --- a/code/modules/fishing/sources/source_types.dm +++ b/code/modules/fishing/sources/source_types.dm @@ -1,6 +1,7 @@ /datum/fish_source/ocean fish_table = list( - FISHING_DUD = 15, + FISHING_DUD = 11, + /obj/effect/spawner/message_in_a_bottle = 4, /obj/item/coin/gold = 7, /obj/item/fish/clownfish = 15, /obj/item/fish/pufferfish = 15, @@ -11,7 +12,7 @@ /obj/item/fish/zipzap = 7, /obj/item/fish/clownfish/lube = 5, /obj/item/fish/swordfish = 5, - /obj/structure/mystery_box/fishing = 1, + /obj/structure/mystery_box/fishing = 2, ) fish_counts = list( /obj/item/fish/clownfish/lube = 2, @@ -96,7 +97,8 @@ /datum/fish_source/portal/beach fish_table = list( - FISHING_DUD = 10, + FISHING_DUD = 7, + /obj/effect/spawner/message_in_a_bottle = 3, /obj/item/fish/clownfish = 10, /obj/item/fish/pufferfish = 10, /obj/item/fish/cardinal = 10, @@ -107,6 +109,12 @@ radial_state = "palm_beach" overlay_state = "portal_beach" +/datum/fish_source/portal/beach/on_fishing_spot_init(datum/component/fishing_spot/spot) + ADD_TRAIT(spot.parent, TRAIT_MESSAGE_IN_A_BOTTLE_LOCATION, INNATE_TRAIT) + +/datum/fish_source/portal/beach/on_fishing_spot_del(datum/component/fishing_spot/spot) + REMOVE_TRAIT(spot.parent, TRAIT_MESSAGE_IN_A_BOTTLE_LOCATION, INNATE_TRAIT) + /datum/fish_source/portal/chasm background = "background_lavaland" fish_table = list( @@ -123,7 +131,8 @@ /datum/fish_source/portal/ocean fish_table = list( - FISHING_DUD = 5, + FISHING_DUD = 3, + /obj/effect/spawner/message_in_a_bottle = 2, /obj/item/fish/lanternfish = 5, /obj/item/fish/firefish = 5, /obj/item/fish/dwarf_moonfish = 5, @@ -146,6 +155,12 @@ overlay_state = "portal_ocean" radial_state = "seaboat" +/datum/fish_source/portal/ocean/on_fishing_spot_init(datum/component/fishing_spot/spot) + ADD_TRAIT(spot.parent, TRAIT_MESSAGE_IN_A_BOTTLE_LOCATION, INNATE_TRAIT) + +/datum/fish_source/portal/ocean/on_fishing_spot_del(datum/component/fishing_spot/spot) + REMOVE_TRAIT(spot.parent, TRAIT_MESSAGE_IN_A_BOTTLE_LOCATION, INNATE_TRAIT) + /datum/fish_source/portal/hyperspace fish_table = list( FISHING_DUD = 5, diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_drink.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_drink.dm index e5589fcc42d..5a754361ffb 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_drink.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_drink.dm @@ -1,6 +1,10 @@ // This is the home of drink related tablecrafting recipes, I have opted to only let players bottle fancy boozes to reduce the number of entries. +///Abstract types for all drink recipes that use bottles and result in another bottle, so that the message_in_a_bottle item is properly transferred. +/datum/crafting_recipe/bottled + parts = list(/obj/item/reagent_containers/cup/glass/bottle = 1) + ///////////////// Booze & Bottles /////////////////// /datum/crafting_recipe/lizardwine @@ -14,7 +18,7 @@ result = /obj/item/reagent_containers/cup/glass/bottle/lizardwine category = CAT_DRINK -/datum/crafting_recipe/moonshinejug +/datum/crafting_recipe/bottled/moonshinejug name = "Moonshine Jug" time = 30 reqs = list( @@ -24,7 +28,7 @@ result = /obj/item/reagent_containers/cup/glass/bottle/moonshine category = CAT_DRINK -/datum/crafting_recipe/hoochbottle +/datum/crafting_recipe/bottled/hoochbottle name = "Hooch Bottle" time = 30 reqs = list( @@ -35,7 +39,7 @@ result = /obj/item/reagent_containers/cup/glass/bottle/hooch category = CAT_DRINK -/datum/crafting_recipe/blazaambottle +/datum/crafting_recipe/bottled/blazaambottle name = "Blazaam Bottle" time = 20 reqs = list( @@ -45,7 +49,7 @@ result = /obj/item/reagent_containers/cup/glass/bottle/blazaam category = CAT_DRINK -/datum/crafting_recipe/champagnebottle +/datum/crafting_recipe/bottled/champagnebottle name = "Champagne Bottle" time = 30 reqs = list( @@ -55,7 +59,7 @@ result = /obj/item/reagent_containers/cup/glass/bottle/champagne category = CAT_DRINK -/datum/crafting_recipe/trappistbottle +/datum/crafting_recipe/bottled/trappistbottle name = "Trappist Bottle" time = 15 reqs = list( @@ -65,7 +69,7 @@ result = /obj/item/reagent_containers/cup/glass/bottle/trappist category = CAT_DRINK -/datum/crafting_recipe/goldschlagerbottle +/datum/crafting_recipe/bottled/goldschlagerbottle name = "Goldschlager Bottle" time = 30 reqs = list( @@ -75,7 +79,7 @@ result = /obj/item/reagent_containers/cup/glass/bottle/goldschlager category = CAT_DRINK -/datum/crafting_recipe/patronbottle +/datum/crafting_recipe/bottled/patronbottle name = "Patron Bottle" time = 30 reqs = list( @@ -87,7 +91,7 @@ ////////////////////// Non-alcoholic recipes /////////////////// -/datum/crafting_recipe/holybottle +/datum/crafting_recipe/bottled/holybottle name = "Holy Water Flask" time = 30 reqs = list( @@ -99,7 +103,7 @@ //flask of unholy water is a beaker for some reason, I will try making it a bottle and add it here once the antag freeze is over. t. kryson -/datum/crafting_recipe/nothingbottle +/datum/crafting_recipe/bottled/nothingbottle name = "Nothing Bottle" time = 30 reqs = list( @@ -116,7 +120,7 @@ reqs = list(/obj/item/stack/sheet/cardboard = 1) category = CAT_CONTAINERS -/datum/crafting_recipe/candycornliquor +/datum/crafting_recipe/bottled/candycornliquor name = "candy corn liquor" result = /obj/item/reagent_containers/cup/glass/bottle/candycornliquor time = 30 @@ -125,7 +129,7 @@ /obj/item/reagent_containers/cup/glass/bottle = 1) category = CAT_DRINK -/datum/crafting_recipe/kong +/datum/crafting_recipe/bottled/kong name = "Kong" result = /obj/item/reagent_containers/cup/glass/bottle/kong time = 30 diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index fe2de7e7520..403d6d269ed 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -64,6 +64,9 @@ ///If TRUE, staff can read paper everywhere, but usually from requests panel. var/request_state = FALSE + ///If this paper can be selected as a candidate for a future message in a bottle when spawned outside of mapload. Doesn't affect manually doing that. + var/can_become_message_in_bottle = TRUE + /obj/item/paper/Initialize(mapload) . = ..() pixel_x = base_pixel_x + rand(-9, 9) @@ -74,10 +77,14 @@ update_appearance() + if(can_become_message_in_bottle && !mapload && prob(MESSAGE_BOTTLE_CHANCE)) + LAZYADD(SSpersistence.queued_message_bottles, src) + /obj/item/paper/Destroy() - . = ..() camera_holder = null clear_paper() + LAZYREMOVE(SSpersistence.queued_message_bottles, src) + return ..() /// Determines whether this paper has been written or stamped to. /obj/item/paper/proc/is_empty() @@ -527,22 +534,10 @@ static_data["user_name"] = user.real_name - static_data["raw_text_input"] = list() - for(var/datum/paper_input/text_input as anything in raw_text_inputs) - static_data["raw_text_input"] += list(text_input.to_list()) - - static_data["raw_field_input"] = list() - for(var/datum/paper_field/field_input as anything in raw_field_input_data) - static_data["raw_field_input"] += list(field_input.to_list()) - - static_data["raw_stamp_input"] = list() - for(var/datum/paper_stamp/stamp_input as anything in raw_stamp_data) - static_data["raw_stamp_input"] += list(stamp_input.to_list()) + static_data += convert_to_data() static_data["max_length"] = MAX_PAPER_LENGTH static_data["max_input_field_length"] = MAX_PAPER_INPUT_FIELD_LENGTH - static_data["paper_color"] = color ? color : COLOR_WHITE - static_data["paper_name"] = name static_data["default_pen_font"] = PEN_FONT static_data["default_pen_color"] = COLOR_BLACK @@ -550,6 +545,43 @@ return static_data; +/obj/item/paper/proc/convert_to_data() + var/list/data = list() + + data[LIST_PAPER_RAW_TEXT_INPUT] = list() + for(var/datum/paper_input/text_input as anything in raw_text_inputs) + data[LIST_PAPER_RAW_TEXT_INPUT] += list(text_input.to_list()) + + data[LIST_PAPER_RAW_FIELD_INPUT] = list() + for(var/datum/paper_field/field_input as anything in raw_field_input_data) + data[LIST_PAPER_RAW_FIELD_INPUT] += list(field_input.to_list()) + + data[LIST_PAPER_RAW_STAMP_INPUT] = list() + for(var/datum/paper_stamp/stamp_input as anything in raw_stamp_data) + data[LIST_PAPER_RAW_STAMP_INPUT] += list(stamp_input.to_list()) + + data[LIST_PAPER_COLOR] = color ? color : COLOR_WHITE + data[LIST_PAPER_NAME] = name + + return data + +/obj/item/paper/proc/write_from_data(list/data) + for(var/list/input as anything in data[LIST_PAPER_RAW_TEXT_INPUT]) + add_raw_text(input[LIST_PAPER_RAW_TEXT], input[LIST_PAPER_FONT], input[LIST_PAPER_FIELD_COLOR], input[LIST_PAPER_BOLD], input[LIST_PAPER_ADVANCED_HTML]) + + for(var/list/field as anything in data[LIST_PAPER_RAW_FIELD_INPUT]) + var/list/input = field[LIST_PAPER_FIELD_DATA] + add_field_input(field[LIST_PAPER_FIELD_INDEX], input[LIST_PAPER_RAW_TEXT], input[LIST_PAPER_FONT], input[LIST_PAPER_FIELD_COLOR], input[LIST_PAPER_BOLD], field[LIST_PAPER_IS_SIGNATURE]) + + for(var/list/stamp as anything in data[LIST_PAPER_RAW_STAMP_INPUT]) + add_stamp(stamp[LIST_PAPER_CLASS], stamp[LIST_PAPER_STAMP_X], stamp[LIST_PAPER_STAMP_Y], stamp[LIST_PAPER_ROTATION]) + + var/new_color = data[LIST_PAPER_COLOR] + if(new_color != COLOR_WHITE) + add_atom_colour(new_color, FIXED_COLOUR_PRIORITY) + + name = data[LIST_PAPER_NAME] + /obj/item/paper/ui_data(mob/user) var/list/data = list() @@ -753,11 +785,11 @@ /datum/paper_input/proc/to_list() return list( - raw_text = raw_text, - font = font, - color = colour, - bold = bold, - advanced_html = advanced_html, + LIST_PAPER_RAW_TEXT = raw_text, + LIST_PAPER_FONT = font, + LIST_PAPER_FIELD_COLOR = colour, + LIST_PAPER_BOLD = bold, + LIST_PAPER_ADVANCED_HTML = advanced_html, ) /// Returns the raw contents of the input as html, with **ZERO SANITIZATION** @@ -793,10 +825,10 @@ /datum/paper_stamp/proc/to_list() return list( - class = class, - x = stamp_x, - y = stamp_y, - rotation = rotation, + LIST_PAPER_CLASS = class, + LIST_PAPER_STAMP_X = stamp_x, + LIST_PAPER_STAMP_Y = stamp_y, + LIST_PAPER_ROTATION = rotation, ) /// A reference to some data that replaces a modifiable input field at some given index in paper raw input parsing. @@ -818,9 +850,9 @@ /datum/paper_field/proc/to_list() return list( - field_index = field_index, - field_data = field_data.to_list(), - is_signature = is_signature, + LIST_PAPER_FIELD_INDEX = field_index, + LIST_PAPER_FIELD_DATA = field_data.to_list(), + LIST_PAPER_IS_SIGNATURE = is_signature, ) /obj/item/paper/construction diff --git a/code/modules/photography/photos/photo.dm b/code/modules/photography/photos/photo.dm index b009c5b2e79..adc99a08cf2 100644 --- a/code/modules/photography/photos/photo.dm +++ b/code/modules/photography/photos/photo.dm @@ -9,12 +9,21 @@ w_class = WEIGHT_CLASS_TINY resistance_flags = FLAMMABLE max_integrity = 50 + drop_sound = 'sound/items/handling/paper_drop.ogg' + pickup_sound = 'sound/items/handling/paper_pickup.ogg' grind_results = list(/datum/reagent/iodine = 4) var/datum/picture/picture var/scribble //Scribble on the back. /obj/item/photo/Initialize(mapload, datum/picture/P, datum_name = TRUE, datum_desc = TRUE) set_picture(P, datum_name, datum_desc, TRUE) + //Photos are quite rarer than papers, so they're more likely to be added to the queue to make things even. + if(!mapload && prob(MESSAGE_BOTTLE_CHANCE * 5) && picture?.id) + LAZYADD(SSpersistence.queued_message_bottles, src) + return ..() + +/obj/item/photo/Destroy() + LAZYREMOVE(SSpersistence.queued_message_bottles, src) return ..() /obj/item/photo/proc/set_picture(datum/picture/P, setname, setdesc, name_override = FALSE) diff --git a/code/modules/reagents/reagent_containers/cups/glassbottle.dm b/code/modules/reagents/reagent_containers/cups/glassbottle.dm index 7b2183b1f22..a214ea47eb1 100644 --- a/code/modules/reagents/reagent_containers/cups/glassbottle.dm +++ b/code/modules/reagents/reagent_containers/cups/glassbottle.dm @@ -39,11 +39,93 @@ var/bottle_knockdown_duration = BOTTLE_KNOCKDOWN_DEFAULT_DURATION tool_behaviour = TOOL_ROLLINGPIN // Used to knock out the Chef. toolspeed = 1.3 //it's a little awkward to use, but it's a cylinder alright. + /// A contained piece of paper, a photo, or space cash, that we can use as a message or gift to future spessmen. + var/obj/item/message_in_a_bottle /obj/item/reagent_containers/cup/glass/bottle/Initialize(mapload, vol) . = ..() var/static/list/recipes = list(/datum/crafting_recipe/molotov) AddElement(/datum/element/slapcrafting, recipes) + register_context() + register_item_context() + +/obj/item/reagent_containers/cup/glass/bottle/add_context(atom/source, list/context, obj/item/held_item, mob/living/user) + if(message_in_a_bottle) + return NONE + if(istype(held_item, /obj/item/paper) || istype(held_item, /obj/item/stack/spacecash) || istype(held_item, /obj/item/photo)) + context[SCREENTIP_CONTEXT_LMB] = "Insert message" + return CONTEXTUAL_SCREENTIP_SET + return NONE + +/obj/item/reagent_containers/cup/glass/bottle/add_item_context(obj/item/source, list/context, atom/target, mob/living/user) + if(message_in_a_bottle && HAS_TRAIT(target, TRAIT_MESSAGE_IN_A_BOTTLE_LOCATION)) + context[SCREENTIP_CONTEXT_RMB] = "Toss message" + return CONTEXTUAL_SCREENTIP_SET + return NONE + +/obj/item/reagent_containers/cup/glass/bottle/Exited(atom/movable/gone, atom/newloc) + if(gone == message_in_a_bottle) + message_in_a_bottle = null + if(!QDELETED(src)) + update_icon(UPDATE_OVERLAYS) + return ..() + +/obj/item/reagent_containers/cup/glass/bottle/CheckParts(list/parts_list) + . = ..() + var/obj/item/reagent_containers/cup/glass/bottle/bottle = locate() in contents + if(bottle.message_in_a_bottle) + message_in_a_bottle = bottle.message_in_a_bottle + bottle.message_in_a_bottle.forceMove(src) + +/obj/item/reagent_containers/cup/glass/bottle/examine(mob/user) + . = ..() + if(message_in_a_bottle) + . += span_info("there's \a [message_in_a_bottle] inside it. Break it to take it out, or find a beach or ocean and toss it with [EXAMINE_HINT("right-click")].") + else if(isGlass) + . += span_tinynoticeital("you could place a paper, photo or space cash inside it...") + +/obj/item/reagent_containers/cup/glass/bottle/update_overlays() + . = ..() + if(message_in_a_bottle) + var/overlay = add_message_overlay() + if(overlay) + . += overlay + +/obj/item/reagent_containers/cup/glass/bottle/interact_with_atom_secondary(atom/target, mob/living/user, list/modifiers) + if(user.combat_mode || !HAS_TRAIT(target, TRAIT_MESSAGE_IN_A_BOTTLE_LOCATION)) + return ..() + if(!user.temporarilyRemoveItemFromInventory(src)) + balloon_alert(user, "it's stuck to your hand!") + return ITEM_INTERACT_BLOCKING + user.visible_message(span_notice("[user] tosses [src] in [target]"), span_notice("You toss [src] in [target]"), span_notice("you hear a splash.")) + SSpersistence.save_message_bottle(message_in_a_bottle, type) + playsound(target, 'sound/effects/bigsplash.ogg', 70) + qdel(src) + return ITEM_INTERACT_SUCCESS + +/obj/item/reagent_containers/cup/glass/bottle/item_interaction(mob/living/user, obj/item/item, list/modifiers) + if(!isGlass) + return NONE + if(!istype(item, /obj/item/paper) && !istype(item, /obj/item/stack/spacecash) && !istype(item, /obj/item/photo)) + return NONE + if(message_in_a_bottle) + balloon_alert(user, "has a message already!") + return ITEM_INTERACT_BLOCKING + if(!user.transferItemToLoc(item, src)) + balloon_alert(user, "it's stuck to your hand!") + return ITEM_INTERACT_BLOCKING + balloon_alert(user, "message inserted") + message_in_a_bottle = item + update_icon(UPDATE_OVERLAYS) + return ITEM_INTERACT_SUCCESS + +/obj/item/reagent_containers/cup/glass/bottle/proc/add_message_overlay() + if(istype(message_in_a_bottle, /obj/item/paper)) + return "paper_in_bottle" + if(istype(message_in_a_bottle, /obj/item/photo)) + return "photo_in_bottle" + if(istype(message_in_a_bottle, /obj/item/stack/spacecash)) + return "cash_in_bottle" /obj/item/reagent_containers/cup/glass/bottle/small name = "small glass bottle" @@ -56,14 +138,16 @@ if(bartender_check(target) && ranged) return SplashReagents(target, ranged, override_spillable = TRUE) - var/obj/item/broken_bottle/B = new(drop_location()) + var/obj/item/broken_bottle/broken = new(drop_location()) if(!ranged && thrower) - thrower.put_in_hands(B) - B.mimic_broken(src, target, break_top) - B.inhand_icon_state = broken_inhand_icon_state + thrower.put_in_hands(broken) + broken.mimic_broken(src, target, break_top) + broken.inhand_icon_state = broken_inhand_icon_state + if(message_in_a_bottle) + message_in_a_bottle.forceMove(drop_location()) qdel(src) - target.Bumped(B) + target.Bumped(broken) /obj/item/reagent_containers/cup/glass/bottle/try_splash(mob/living/user, atom/target) @@ -339,6 +423,9 @@ list_reagents = list(/datum/reagent/water/holywater = 100) drink_type = NONE +/obj/item/reagent_containers/cup/glass/bottle/holywater/add_message_overlay() + return //looks too weird... + /obj/item/reagent_containers/cup/glass/bottle/holywater/hell desc = "A flask of holy water...it's been sitting in the Necropolis a while though." icon_state = "unholyflask" @@ -501,7 +588,6 @@ list_reagents = list(/datum/reagent/consumable/ethanol/sake = 100) /obj/item/reagent_containers/cup/glass/bottle/sake/Initialize(mapload) - . = ..() if(prob(10)) name = "Fluffy Tail Sake" desc += " On the bottle is a picture of a kitsune with nine touchable tails." @@ -510,6 +596,12 @@ name = "Inubashiri's Home Brew" desc += " Awoo." icon_state = "sakebottle_i" + return ..() + +/obj/item/reagent_containers/cup/glass/bottle/sake/add_message_overlay() + if(icon_state == "sakebottle_k") //doesn't fit the sprite + return + return ..() /obj/item/reagent_containers/cup/glass/bottle/fernet name = "Fernet Bronca" @@ -530,6 +622,9 @@ icon_state = "curacao_bottle" list_reagents = list(/datum/reagent/consumable/ethanol/curacao = 100) +/obj/item/reagent_containers/cup/glass/bottle/curacao/add_message_overlay() + return //doesn't fit the sprite + /obj/item/reagent_containers/cup/glass/bottle/navy_rum name = "Pride of the Union Navy-Strength Rum" desc = "Ironically named, given it's made in Bermuda." @@ -574,6 +669,9 @@ ///Whether this bottle was a victim of a successful sabrage attempt var/sabraged = FALSE +/obj/item/reagent_containers/cup/glass/bottle/champagne/add_message_overlay() + return //doesn't stylistically fit the sprite + /obj/item/reagent_containers/cup/glass/bottle/champagne/cursed sabrage_success_percentile = 0 //force of the sharp item used to sabrage will not increase success chance @@ -733,12 +831,18 @@ icon_state = "hoochbottle" list_reagents = list(/datum/reagent/consumable/ethanol/hooch = 100) +/obj/item/reagent_containers/cup/glass/bottle/hooch/add_message_overlay() + return //doesn't fit the sprite + /obj/item/reagent_containers/cup/glass/bottle/moonshine name = "moonshine jug" desc = "It is said that the ancient Applalacians used these stoneware jugs to capture lightning in a bottle." icon_state = "moonshinebottle" list_reagents = list(/datum/reagent/consumable/ethanol/moonshine = 100) +/obj/item/reagent_containers/cup/glass/bottle/moonshine/add_message_overlay() + return //doesn't fit the sprite + /obj/item/reagent_containers/cup/glass/bottle/mushi_kombucha name = "Solzara Brewing Company Mushi Kombucha" desc = "Best drunk over ice to savour the mushroomy flavour." @@ -790,15 +894,15 @@ ) /obj/item/reagent_containers/cup/glass/bottle/molotov/CheckParts(list/parts_list) - ..() - var/obj/item/reagent_containers/cup/glass/bottle/B = locate() in contents - if(B) - icon_state = B.icon_state - B.reagents.copy_to(src, 100) - if(istype(B, /obj/item/reagent_containers/cup/glass/bottle/juice)) - desc += " You're not sure if making this out of a carton was the brightest idea." - isGlass = FALSE - return + . = ..() + var/obj/item/reagent_containers/cup/glass/bottle/bottle = locate() in contents + if(!bottle) + return + icon_state = bottle.icon_state + bottle.reagents.copy_to(src, 100) + if(istype(bottle, /obj/item/reagent_containers/cup/glass/bottle/juice)) + desc += " You're not sure if making this out of a carton was the brightest idea." + isGlass = FALSE /obj/item/reagent_containers/cup/glass/bottle/molotov/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum, do_splash = FALSE) ..(hit_atom, throwingdatum, do_splash = FALSE) diff --git a/icons/effects/random_spawners.dmi b/icons/effects/random_spawners.dmi index 4e560833003..d6e06fb140d 100644 Binary files a/icons/effects/random_spawners.dmi and b/icons/effects/random_spawners.dmi differ diff --git a/icons/hud/fishing_hud.dmi b/icons/hud/fishing_hud.dmi index 0a952615d58..db36e241a2f 100644 Binary files a/icons/hud/fishing_hud.dmi and b/icons/hud/fishing_hud.dmi differ diff --git a/icons/obj/drinks/bottles.dmi b/icons/obj/drinks/bottles.dmi index cd3cf9fef09..2bdacf04265 100644 Binary files a/icons/obj/drinks/bottles.dmi and b/icons/obj/drinks/bottles.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 204142715ac..2ebb1b85729 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -740,6 +740,7 @@ #include "code\controllers\subsystem\persistence\counter_tram_hits.dm" #include "code\controllers\subsystem\persistence\custom_outfits.dm" #include "code\controllers\subsystem\persistence\engravings.dm" +#include "code\controllers\subsystem\persistence\message_bottles.dm" #include "code\controllers\subsystem\persistence\photo_albums.dm" #include "code\controllers\subsystem\persistence\piggy_banks.dm" #include "code\controllers\subsystem\persistence\recipes.dm" @@ -2291,6 +2292,7 @@ #include "code\game\objects\effects\spawners\bombspawner.dm" #include "code\game\objects\effects\spawners\costume.dm" #include "code\game\objects\effects\spawners\gibspawner.dm" +#include "code\game\objects\effects\spawners\message_in_a_bottle.dm" #include "code\game\objects\effects\spawners\structure.dm" #include "code\game\objects\effects\spawners\xeno_egg_delivery.dm" #include "code\game\objects\effects\spawners\random\ai_module.dm"