From 9d6f1645013d7c6140ce1955ca1d4ed6f669cad4 Mon Sep 17 00:00:00 2001 From: Ghom <42542238+Ghommie@users.noreply.github.com> Date: Sat, 4 Jan 2025 18:51:38 +0100 Subject: [PATCH] You can now try to fish random products out of vending machines with a fishing rod and a few bucks. (#88290) ## About The Pull Request You can now use a fishing rod to fish random vending products from vending machines, using holochips, coins and spacecash as bait. The value of the money used as bait will positively influence the chance of getting the pricier vending items, while using less than the minimum paycheck (25 credits) will increase the minigame difficulty and carries an additional, small risk of getting a dud or even hurling the vending machine in your direction. This also warranted a slight refactor of how vending product prices are set. ## Why It's Good For The Game Expands the list of things you can do with a fishing rod. Like for the organ manipulation fishing PR. This is not about getting fish, but the unconventional things we can do along the way. Now tested, it works. ## Changelog :cl: add: You can now try to fish random products out of vending machines with a fishing rod and a few bucks. fix: Fixing the 1000 cr holochip loot from exploration drones. /:cl: --- code/__DEFINES/economy.dm | 3 + code/__DEFINES/fish.dm | 3 + code/__HELPERS/cmp.dm | 7 + code/controllers/subsystem/economy.dm | 4 +- code/datums/components/fishing_spot.dm | 7 +- code/game/objects/items/credit_holochip.dm | 3 +- code/game/objects/items/stacks/cash.dm | 1 + code/modules/fishing/fish/fish_traits.dm | 4 +- code/modules/fishing/fishing_minigame.dm | 4 +- code/modules/fishing/fishing_rod.dm | 2 +- code/modules/fishing/sources/_fish_source.dm | 12 +- code/modules/fishing/sources/source_types.dm | 175 ++++++++++++++++++- code/modules/mining/ores_coins.dm | 1 + code/modules/vending/_vending.dm | 122 ++++++------- code/modules/vending/sustenance.dm | 2 - icons/hud/radial_fishing.dmi | Bin 9928 -> 11006 bytes strings/fishing_tips.txt | 1 + 17 files changed, 267 insertions(+), 84 deletions(-) diff --git a/code/__DEFINES/economy.dm b/code/__DEFINES/economy.dm index 5ffa92c05b6..356ceb2301a 100644 --- a/code/__DEFINES/economy.dm +++ b/code/__DEFINES/economy.dm @@ -27,6 +27,9 @@ //What should vending machines charge when you buy something in-department. #define DEPARTMENT_DISCOUNT 0.2 +//the amount of credits collected by the vending machines that can be redeemed when restocking it. +#define VENDING_CREDITS_COLLECTION_AMOUNT 0.2 + #define ACCOUNT_CIV "CIV" #define ACCOUNT_CIV_NAME "Civil Budget" #define ACCOUNT_ENG "ENG" diff --git a/code/__DEFINES/fish.dm b/code/__DEFINES/fish.dm index 41a9268e0aa..b71c8aad382 100644 --- a/code/__DEFINES/fish.dm +++ b/code/__DEFINES/fish.dm @@ -7,6 +7,9 @@ ///Used in the dimensional rift fishing spot to define influence gain #define FISHING_INFLUENCE "Influence" +///Represents the chance of getting squashed by the vending machine from the vending machine fish source +#define FISHING_VENDING_CHUCK "thinkfastchucklenuts" + // Baseline fishing difficulty levels #define FISHING_DEFAULT_DIFFICULTY 15 #define FISHING_EASY_DIFFICULTY 10 diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index aa13b74f0e1..7451a99cdd2 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -216,6 +216,13 @@ var/position_b = fluids_priority.Find(initial(b.required_fluid_type)) return cmp_numeric_asc(position_a, position_b) || cmp_text_asc(initial(b.name), initial(a.name)) +/// Orders vending products by their price +/proc/cmp_vending_prices(datum/data/vending_product/a, datum/data/vending_product/b) + return b.price - a.price + +/proc/cmp_item_vending_prices(obj/item/a, obj/item/b) + return b.custom_price - a.custom_price + ///Sorts stock parts based on tier /proc/cmp_rped_sort(obj/item/first_item, obj/item/second_item) ///even though stacks aren't stock parts, get_part_rating() is defined on the item level (see /obj/item/proc/get_part_rating()) and defaults to returning 0. diff --git a/code/controllers/subsystem/economy.dm b/code/controllers/subsystem/economy.dm index ae9afb831f5..2ad4337732a 100644 --- a/code/controllers/subsystem/economy.dm +++ b/code/controllers/subsystem/economy.dm @@ -228,8 +228,8 @@ SUBSYSTEM_DEF(economy) continue prices_to_update += vending_lad for(var/i in 1 to length(prices_to_update)) - var/obj/machinery/vending/V = prices_to_update[i] - V.reset_prices(V.product_records, V.coin_records) + var/obj/machinery/vending/vending = prices_to_update[i] + vending.reset_prices(vending.product_records, vending.coin_records + vending.hidden_records) #undef ECON_DEPARTMENT_STEP #undef ECON_ACCOUNT_STEP diff --git a/code/datums/components/fishing_spot.dm b/code/datums/components/fishing_spot.dm index 3ce68dce339..ebf885f14d4 100644 --- a/code/datums/components/fishing_spot.dm +++ b/code/datums/components/fishing_spot.dm @@ -38,17 +38,14 @@ ///If the fish source has fishes that are shown in the /datum/component/fishing_spot/proc/on_examined(datum/source, mob/user, list/examine_text) SIGNAL_HANDLER - if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISHING_SPOT)) - return - - if(!fish_source.has_known_fishes(source)) + if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISHING_SPOT) || !fish_source.has_known_fishes(source)) return examine_text += span_tinynoticeital("This is a fishing spot. You can look again to list its fishes...") /datum/component/fishing_spot/proc/on_examined_more(datum/source, mob/user, list/examine_text) SIGNAL_HANDLER - if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISHING_SPOT)) + if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISHING_SPOT) || !fish_source.has_known_fishes(source)) return fish_source.get_catchable_fish_names(user, parent, examine_text) diff --git a/code/game/objects/items/credit_holochip.dm b/code/game/objects/items/credit_holochip.dm index 7a57782dd39..9e9b6c8ac90 100644 --- a/code/game/objects/items/credit_holochip.dm +++ b/code/game/objects/items/credit_holochip.dm @@ -13,11 +13,12 @@ /obj/item/holochip/Initialize(mapload, amount = 1) . = ..() - if(amount) + if(!credits && amount) credits = amount if(credits <= 0 && !mapload) stack_trace("Holochip created with 0 or less credits in [get_area_name(src)]!") return INITIALIZE_HINT_QDEL + add_traits(list(TRAIT_FISHING_BAIT, TRAIT_BAIT_ALLOW_FISHING_DUD), INNATE_TRAIT) update_appearance() /obj/item/holochip/examine(mob/user) diff --git a/code/game/objects/items/stacks/cash.dm b/code/game/objects/items/stacks/cash.dm index 754532fe0d1..1a4a3129659 100644 --- a/code/game/objects/items/stacks/cash.dm +++ b/code/game/objects/items/stacks/cash.dm @@ -17,6 +17,7 @@ /obj/item/stack/spacecash/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1) . = ..() + add_traits(list(TRAIT_FISHING_BAIT, TRAIT_BAIT_ALLOW_FISHING_DUD), INNATE_TRAIT) update_desc() /obj/item/stack/spacecash/update_desc() diff --git a/code/modules/fishing/fish/fish_traits.dm b/code/modules/fishing/fish/fish_traits.dm index aef5dd583f1..d70b6b119f7 100644 --- a/code/modules/fishing/fish/fish_traits.dm +++ b/code/modules/fishing/fish/fish_traits.dm @@ -120,12 +120,12 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/shiny_lover name = "Shiny Lover" - catalog_description = "This fish loves shiny things, shiny lure recommended." + catalog_description = "This fish loves shiny things and money, shiny lure recommended." /datum/fish_trait/shiny_lover/difficulty_mod(obj/item/fishing_rod/rod, mob/fisherman) . = ..() // These fish are easier to catch with shiny hook - if(HAS_TRAIT(rod, TRAIT_ROD_ATTRACT_SHINY_LOVERS)) + if(HAS_TRAIT(rod, TRAIT_ROD_ATTRACT_SHINY_LOVERS) || (rod.bait?.get_item_credit_value() >= PAYCHECK_CREW)) .[ADDITIVE_FISHING_MOD] -= FISH_TRAIT_MINOR_DIFFICULTY_BOOST /datum/fish_trait/shiny_lover/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman) diff --git a/code/modules/fishing/fishing_minigame.dm b/code/modules/fishing/fishing_minigame.dm index 75cd0043d22..a975063a274 100644 --- a/code/modules/fishing/fishing_minigame.dm +++ b/code/modules/fishing/fishing_minigame.dm @@ -399,8 +399,8 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user) if(HAS_MIND_TRAIT(user, TRAIT_REVEAL_FISH)) var/possible_icon - if(isatom(reward_path)) - var/atom/reward = reward_path + if(isdatum(reward_path)) + var/datum/reward = reward_path possible_icon = GLOB.specific_fish_icons[reward.type] else possible_icon = GLOB.specific_fish_icons[reward_path] diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm index f527db6e6c3..50fc0c2a22f 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/code/modules/fishing/fishing_rod.dm @@ -263,7 +263,7 @@ if(kill_fish) fish.set_status(FISH_DEAD, silent = TRUE) - QDEL_NULL(bait) + qdel(bait) update_icon() ///Returns the probability that a fish caught by this (custom material) rod will be of the same material. diff --git a/code/modules/fishing/sources/_fish_source.dm b/code/modules/fishing/sources/_fish_source.dm index 00387c1cffe..c7474509863 100644 --- a/code/modules/fishing/sources/_fish_source.dm +++ b/code/modules/fishing/sources/_fish_source.dm @@ -12,6 +12,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) /proc/generate_specific_fish_icons() var/list/return_list = zebra_typecacheof(list( + /datum/data/vending_product = FISH_ICON_COIN, /mob/living/basic/axolotl = FISH_ICON_CRITTER, /mob/living/basic/frog = FISH_ICON_CRITTER, /mob/living/basic/carp = FISH_ICON_DEF, @@ -61,6 +62,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) return_list[FISHING_RANDOM_SEED] = FISH_ICON_SEED return_list[FISHING_RANDOM_ORGAN] = FISH_ICON_ORGAN + return_list[FISHING_VENDING_CHUCK] = FISH_ICON_COIN return return_list /** @@ -166,17 +168,13 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) * * For non-fish, it's just the source's fishing difficulty minus the fisherman skill. */ -/datum/fish_source/proc/calculate_difficulty(result, obj/item/fishing_rod/rod, mob/fisherman, datum/fishing_challenge/challenge) +/datum/fish_source/proc/calculate_difficulty(result, obj/item/fishing_rod/rod, mob/fisherman) . = fishing_difficulty // Difficulty modifier added by having the Settler quirk if(HAS_TRAIT(fisherman, TRAIT_EXPERT_FISHER)) . += EXPERT_FISHER_DIFFICULTY_MOD - // Difficulty modifier added by the fisher's skill level - if(!(challenge?.special_effects & FISHING_MINIGAME_RULE_NO_EXP)) - . += fisherman.mind?.get_skill_modifier(/datum/skill/fishing, SKILL_VALUE_MODIFIER) - // Difficulty modifier added by the rod . += rod.difficulty_modifier @@ -257,14 +255,14 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) UnregisterSignal(user, COMSIG_MOB_COMPLETE_FISHING) if(!success) return - var/atom/movable/reward = dispense_reward(challenge.reward_path, user, challenge.location) + var/atom/movable/reward = dispense_reward(challenge.reward_path, user, challenge.location, challenge.used_rod) if(reward) user.add_mob_memory(/datum/memory/caught_fish, protagonist = user, deuteragonist = reward.name) SEND_SIGNAL(challenge.used_rod, COMSIG_FISHING_ROD_CAUGHT_FISH, reward, user) challenge.used_rod.on_reward_caught(reward, user) /// Gives out the reward if possible -/datum/fish_source/proc/dispense_reward(reward_path, mob/fisherman, atom/fishing_spot) +/datum/fish_source/proc/dispense_reward(reward_path, mob/fisherman, atom/fishing_spot, obj/item/fishing_rod/rod) var/atom/movable/reward = simple_dispense_reward(reward_path, get_turf(fisherman), fishing_spot) if(!reward) //balloon alert instead fisherman.balloon_alert(fisherman, pick(duds)) diff --git a/code/modules/fishing/sources/source_types.dm b/code/modules/fishing/sources/source_types.dm index 2c3833080f1..af975bb4ccd 100644 --- a/code/modules/fishing/sources/source_types.dm +++ b/code/modules/fishing/sources/source_types.dm @@ -701,6 +701,7 @@ #define RANDOM_AQUARIUM_FISH "random_aquarium_fish" /datum/fish_source/aquarium + catalog_description = "Aquariums" radial_state = "fish_tank" fish_table = list( FISHING_DUD = 10, @@ -720,7 +721,7 @@ continue table[fish] = 10 if(!length(table)) - return fish_table + return fish_table.Copy() return table /datum/fish_source/aquarium/generate_wiki_contents(datum/autowiki/fish_sources/wiki) @@ -771,6 +772,176 @@ fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 20 fish_source_flags = FISH_SOURCE_FLAG_EXPLOSIVE_MALUS +/datum/fish_source/vending + catalog_description = "Vending Machines" + radial_state = "vending" + overlay_state = "portal_randomizer" + fish_table = list( + FISHING_DUD = 10, + ) + fish_source_flags = FISH_SOURCE_FLAG_NO_BLUESPACE_ROD|FISH_SOURCE_FLAG_EXPLOSIVE_NONE + fishing_difficulty = FISHING_EASY_DIFFICULTY //with some equipment and just enough dosh, you should be able to skip the minigame + +/datum/fish_source/vending/generate_wiki_contents(datum/autowiki/fish_sources/wiki) + var/list/data = list() + + data += LIST_VALUE_WRAP_LISTS(list( + FISH_SOURCE_AUTOWIKI_NAME = "Vending Products", + FISH_SOURCE_AUTOWIKI_DUD = "", + FISH_SOURCE_AUTOWIKI_WEIGHT = 100, + FISH_SOURCE_AUTOWIKI_NOTES = "Use chips, bills or coins as bait to get a semi-random vending product, depending on both its and the bait's monetary values", + )) + + return data + +/datum/fish_source/vending/get_modified_fish_table(obj/item/fishing_rod/rod, mob/fisherman, atom/location) + if(istype(location, /obj/machinery/fishing_portal_generator)) + var/obj/machinery/fishing_portal_generator/portal = location + location = portal.current_linked_atom + if(!istype(location, /obj/machinery/vending)) + return list() + + return get_vending_table(rod, fisherman, location) + +/datum/fish_source/vending/proc/get_vending_table(obj/item/fishing_rod/rod, mob/fisherman, obj/machinery/vending/location) + var/list/table = list() + ///Create a list of products, ordered by price from highest to lowest + var/list/products = location.product_records + location.coin_records + location.hidden_records + sortTim(products, GLOBAL_PROC_REF(cmp_vending_prices)) + + var/bait_value = rod.bait?.get_item_credit_value() || 1 + + var/highest_record_price = 0 + for(var/datum/data/vending_product/product_record as anything in products) + if(product_record.amount <= 0) + products -= product_record + table[FISHING_DUD] += PAYCHECK_LOWER //it gets harder the emptier the machine is + continue + if(!highest_record_price) + highest_record_price = product_record.price + var/high = max(highest_record_price, bait_value) + var/low = min(highest_record_price, bait_value) + + //the smaller the difference between product price and bait value, the more likely you're to get it. + table[product_record] = low/high * 1000 //multiply the value by 1000 for accuracy. pick_weight() doesn't work with zero decimals yet. + + add_risks(table, bait_value, highest_record_price, length(products) * 0.5) + return table + +/datum/fish_source/vending/proc/add_risks(list/table, bait_value, highest_price, malus_multiplier) + ///Using more than the money needed to buy the most expensive item (why would you do it?!) will remove the dud chance. + if(bait_value > highest_price) + table -= FISHING_DUD + else + //Makes using 1 cred chips with the minigame skip (negative fishing difficulty) a bit less cheesy. + var/malus = min(PAYCHECK_LOWER - bait_value, highest_price) + if(malus > 0) + table[FISHING_DUD] += malus * malus_multiplier + table[FISHING_VENDING_CHUCK] += malus * malus_multiplier + +#define FISHING_PRODUCT_DIFFICULTY_MULT 1.6 + +/datum/fish_source/vending/calculate_difficulty(datum/fishing_challenge/challenge, result, obj/item/fishing_rod/rod, mob/fisherman) + //Using less than a minimum paycheck is going to make the challenge a tad harder. + var/bait_value = rod.bait?.get_item_credit_value() + var/base_diff = PAYCHECK_LOWER - bait_value + return ..() + get_product_difficulty(base_diff, result) * FISHING_PRODUCT_DIFFICULTY_MULT + +/datum/fish_source/vending/proc/get_product_difficulty(diff, datum/result) + if(istype(result, /datum/data/vending_product)) + var/datum/data/vending_product/product = result + diff = min(diff, product.price) // low priced items are easier to catch anyway + return diff + +#undef FISHING_PRODUCT_DIFFICULTY_MULT + +/datum/fish_source/vending/dispense_reward(reward_path, mob/fisherman, atom/fishing_spot, obj/item/fishing_rod/rod) + var/obj/machinery/vending/vending = fishing_spot + if(istype(fishing_spot, /obj/machinery/fishing_portal_generator)) + var/obj/machinery/fishing_portal_generator/portal = fishing_spot + vending = portal.current_linked_atom + + if(reward_path == FISHING_VENDING_CHUCK) + if(fishing_spot != vending) //fishing portals + vending.forceMove(get_turf(fishing_spot)) + vending.tilt(fisherman, range = 4) + return null //Don't spawn a reward at all + + var/atom/movable/reward = ..() + if(reward) + var/creds_value = rod.bait?.get_item_credit_value() + if(creds_value) + vending.credits_contained += round(creds_value * VENDING_CREDITS_COLLECTION_AMOUNT) + qdel(rod.bait) + return reward + +/datum/fish_source/vending/spawn_reward(reward_path, atom/spawn_location, obj/machinery/vending/fishing_spot, obj/item/fishing_rod/used_rod) + if(istype(fishing_spot, /obj/machinery/fishing_portal_generator)) + var/obj/machinery/fishing_portal_generator/portal = fishing_spot + fishing_spot = portal.current_linked_atom + if(!istype(fishing_spot)) + return null + return spawn_vending_reward(reward_path, spawn_location, fishing_spot) + +/datum/fish_source/vending/proc/spawn_vending_reward(reward_path, atom/spawn_location, obj/machinery/vending/fishing_spot) + var/datum/data/vending_product/product_record = reward_path + if(!istype(product_record) || product_record.amount <= 0) + return null + return fishing_spot.dispense(product_record, spawn_location) + +/datum/fish_source/vending/pre_challenge_started(obj/item/fishing_rod/rod, mob/user, datum/fishing_challenge/challenge) + RegisterSignal(rod, COMSIG_FISHING_ROD_CAUGHT_FISH, PROC_REF(on_reward)) + +/datum/fish_source/vending/on_challenge_completed(mob/user, datum/fishing_challenge/challenge, success) + . = ..() + UnregisterSignal(challenge.used_rod, COMSIG_FISHING_ROD_CAUGHT_FISH) + +/datum/fish_source/vending/proc/on_reward(obj/item/fishing_rod/rod, atom/movable/reward, mob/user) + SIGNAL_HANDLER + if(reward && !QDELETED(rod.bait) && rod.bait.get_item_credit_value()) //you pay for what you get + qdel(rod.bait) // fishing_rod.Exited() will handle clearing the hard ref. + +///subtype of fish_source/vending for custom vending machines +/datum/fish_source/vending/custom + catalog_description = null //no duplicate entries on autowiki or catalog + +/datum/fish_source/vending/custom/get_vending_table(obj/item/fishing_rod/rod, mob/fisherman, obj/machinery/vending/location) + var/list/table = list() + ///Create a list of products, ordered by price from highest to lowest + var/list/products = location.vending_machine_input.Copy() + sortTim(products, GLOBAL_PROC_REF(cmp_item_vending_prices)) + + var/bait_value = rod.bait?.get_item_credit_value() || 1 + + var/highest_record_price = 0 + for(var/obj/item/stocked as anything in products) + if(location.vending_machine_input[stocked] <= 0) + products -= stocked + table[FISHING_DUD] += PAYCHECK_LOWER //it gets harder the emptier the machine is + continue + if(!highest_record_price) + highest_record_price = stocked.custom_price + var/high = max(highest_record_price, bait_value) + var/low = min(highest_record_price, bait_value) + + //the smaller the difference between product price and bait value, the more likely you're to get it. + table[stocked] = low/high * 1000 //multiply the value by 1000 for accuracy. pick_weight() doesn't work with zero decimals yet. + + add_risks(table, bait_value, highest_record_price, length(products) * 0.5) + return table + +/datum/fish_source/vending/custom/get_product_difficulty(diff, datum/result) + if(isitem(result)) + var/obj/item/product = result + diff = min(diff, product.custom_price) + return diff + +/datum/fish_source/vending/custom/spawn_vending_reward(obj/item/reward, atom/spawn_location, obj/machinery/vending/fishing_spot) + if(!isitem(reward)) + return null + reward.forceMove(spawn_location) + return reward + /datum/fish_source/dimensional_rift catalog_description = null //it's a secret (sorta, I know you're reading this) radial_state = "cursed" // placeholder @@ -840,7 +1011,7 @@ fire_curse_hand(user, get_turf(challenge.location)) // Handled above -/datum/fish_source/dimensional_rift/spawn_reward(reward_path, atom/spawn_location, atom/fishing_spot) +/datum/fish_source/dimensional_rift/spawn_reward(reward_path, atom/spawn_location, atom/fishing_spot, obj/item/fishing_rod/used_rod) if(reward_path != FISHING_INFLUENCE) return ..() return diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm index 55a1b9557c2..5f5d11ccd02 100644 --- a/code/modules/mining/ores_coins.dm +++ b/code/modules/mining/ores_coins.dm @@ -441,6 +441,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ icon_state = "coin_[coinflip]" pixel_x = base_pixel_x + rand(0, 16) - 8 pixel_y = base_pixel_y + rand(0, 8) - 8 + add_traits(list(TRAIT_FISHING_BAIT, TRAIT_BAIT_ALLOW_FISHING_DUD), INNATE_TRAIT) /obj/item/coin/finalize_material_effects(list/materials) . = ..() diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index bc1bec7a734..89a9b14da5b 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -33,10 +33,8 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) var/amount = 0 ///How many we can store at maximum var/max_amount = 0 - ///Does the item have a custom price override - var/custom_price - ///Does the item have a custom premium price override - var/custom_premium_price + ///The price of the item + var/price ///Whether spessmen with an ID with an age below AGE_MINOR (20 by default) can buy this item var/age_restricted = FALSE ///Whether the product can be recolored by the GAGS system @@ -220,6 +218,9 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) /// used for narcing on underages var/obj/item/radio/sec_radio + //the path of the fish_source datum to use for the fishing_spot component + var/fish_source_path = /datum/fish_source/vending + /datum/armor/machinery_vending melee = 20 fire = 50 @@ -291,6 +292,9 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) GLOB.vending_machines_to_restock += src //We need to keep track of the final onstation vending machines so we can keep them restocked. register_context() + if(fish_source_path) + AddComponent(/datum/component/fishing_spot, fish_source_path) + /obj/machinery/vending/Destroy() QDEL_NULL(coin) QDEL_NULL(bill) @@ -391,13 +395,12 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) var/dump_path = record.product_path if(!dump_path) continue - record.amount-- // busting open a vendor will destroy some of the contents if(found_anything && prob(80)) + record.amount-- continue - var/obj/obj_to_dump = new dump_path(loc) - on_dispense(obj_to_dump) + var/obj/obj_to_dump = dispense(record, loc) step(obj_to_dump, pick(GLOB.alldirs)) found_anything = TRUE dump_amount++ @@ -413,13 +416,12 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) * * recordlist - the list containing /datum/data/vending_product datums * * categories - A list in the format of product_categories to source category from * * startempty - should we set vending_product record amount from the product list (so it's prefilled at roundstart) + * * premium - Whether the ending products shall have premium or default prices */ -/obj/machinery/vending/proc/build_inventory(list/productlist, list/recordlist, list/categories, start_empty = FALSE) - default_price = round(initial(default_price)) - extra_price = round(initial(extra_price)) - if(HAS_TRAIT(SSeconomy, TRAIT_MARKET_CRASHING)) - default_price *= SSeconomy.inflation_value() - extra_price *= SSeconomy.inflation_value() +/obj/machinery/vending/proc/build_inventory(list/productlist, list/recordlist, list/categories, start_empty = FALSE, premium = FALSE) + var/inflation_value = HAS_TRAIT(SSeconomy, TRAIT_MARKET_CRASHING) ? SSeconomy.inflation_value() : 1 + default_price = round(initial(default_price) * inflation_value) + extra_price = round(initial(extra_price) * inflation_value) var/list/product_to_category = list() for (var/list/category as anything in categories) @@ -439,12 +441,18 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) if(!start_empty) new_record.amount = amount new_record.max_amount = amount + ///Prices of vending machines are all increased uniformly. - new_record.custom_price = round(initial(temp.custom_price)) - new_record.custom_premium_price = round(initial(temp.custom_premium_price)) - if(HAS_TRAIT(SSeconomy, TRAIT_MARKET_CRASHING)) - new_record.custom_price = round(initial(temp.custom_price) * SSeconomy.inflation_value()) - new_record.custom_premium_price = round(initial(temp.custom_premium_price) * SSeconomy.inflation_value()) + var/custom_price = round(initial(temp.custom_price) * inflation_value) + if(!premium) + new_record.price = custom_price || default_price + else + var/premium_custom_price = round(initial(temp.custom_premium_price) * inflation_value) + if(!premium_custom_price && custom_price) //For some ungodly reason, some premium only items only have a custom_price + new_record.price = extra_price + custom_price + else + new_record.price = premium_custom_price || extra_price + new_record.age_restricted = initial(temp.age_restricted) new_record.colorable = !!(initial(temp.greyscale_config) && initial(temp.greyscale_colors) && (initial(temp.flags_1) & IS_PLAYER_COLORABLE_1)) new_record.category = product_to_category[typepath] @@ -456,8 +464,8 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) */ /obj/machinery/vending/proc/build_inventories(start_empty) build_inventory(products, product_records, product_categories, start_empty) - build_inventory(contraband, hidden_records, create_categories_from("Contraband", "mask", contraband), start_empty) - build_inventory(premium, coin_records, create_categories_from("Premium", "coins", premium), start_empty) + build_inventory(contraband, hidden_records, create_categories_from("Contraband", "mask", contraband), start_empty, premium = TRUE) + build_inventory(premium, coin_records, create_categories_from("Premium", "coins", premium), start_empty, premium = TRUE) /** * Returns a list of data about the category @@ -494,30 +502,22 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) * * premiumlist - the list of premium product datums in the vendor to refresh their prices. */ /obj/machinery/vending/proc/reset_prices(list/recordlist, list/premiumlist) - var/crash_status = HAS_TRAIT(SSeconomy, TRAIT_MARKET_CRASHING) - default_price = round(initial(default_price)) - extra_price = round(initial(extra_price)) - if(crash_status) - default_price *= SSeconomy.inflation_value() - extra_price *= SSeconomy.inflation_value() + var/inflation_value = HAS_TRAIT(SSeconomy, TRAIT_MARKET_CRASHING) ? SSeconomy.inflation_value() : 1 + default_price = round(initial(default_price) * inflation_value) + extra_price = round(initial(extra_price) * inflation_value) for(var/datum/data/vending_product/record as anything in recordlist) var/obj/item/potential_product = record.product_path - record.custom_price = round(initial(potential_product.custom_price)) - if(crash_status) - record.custom_price = round(initial(potential_product.custom_price) * SSeconomy.inflation_value()) + var/custom_price = round(initial(potential_product.custom_price) * inflation_value) + record.price = custom_price | default_price for(var/datum/data/vending_product/premium_record as anything in premiumlist) var/obj/item/potential_product = premium_record.product_path - var/premium_sanity = round(initial(potential_product.custom_premium_price)) - if(premium_sanity) - premium_record.custom_premium_price = round(premium_sanity) - if(crash_status) - premium_record.custom_premium_price = round(premium_sanity * SSeconomy.inflation_value()) - continue - //For some ungodly reason, some premium only items only have a custom_price - premium_record.custom_premium_price = round(extra_price + (initial(potential_product.custom_price))) - if(crash_status) - premium_record.custom_premium_price = round(extra_price + (initial(potential_product.custom_price) * (SSeconomy.inflation_value() - 1))) + var/premium_custom_price = round(initial(potential_product.custom_premium_price) * inflation_value) + var/custom_price = initial(potential_product.custom_price) + if(!premium_custom_price && custom_price) //For some ungodly reason, some premium only items only have a custom_price + premium_record.price = extra_price + round(custom_price * inflation_value) + else + premium_record.price = premium_custom_price || extra_price /** * Refill a vending machine from a refill canister @@ -793,17 +793,13 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) if(record.amount <= 0) //Try to use a record that actually has something to dump. continue - var/dump_path = record.product_path - if(!dump_path) - continue if(record.amount > LAZYLEN(record.returned_products)) //always give out new stuff that costs before free returned stuff, because of the risk getting gibbed involved - var/obj/item/free_stuff = new dump_path(get_turf(src)) - on_dispense(free_stuff) + dispense(record, get_turf(src), silent = TRUE) else var/obj/returned_obj_to_dump = LAZYACCESS(record.returned_products, LAZYLEN(record.returned_products)) //first in, last out LAZYREMOVE(record.returned_products, returned_obj_to_dump) returned_obj_to_dump.forceMove(get_turf(src)) - record.amount-- + record.amount-- break deploy_credits() @@ -813,8 +809,9 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) * fatty - atom to tilt the vendor onto * local_crit_chance - percent chance of a critical hit * forced_crit - specific critical hit case to use, if any + * range - the range of the machine when thrown if not adjacent */ -/obj/machinery/vending/proc/tilt(atom/fatty, local_crit_chance = crit_chance, forced_crit = forcecrit) +/obj/machinery/vending/proc/tilt(atom/fatty, local_crit_chance = crit_chance, forced_crit = forcecrit, range = 1) if(QDELETED(src) || !has_gravity(src)) return @@ -831,7 +828,7 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) layer = ABOVE_MOB_LAYER if(get_turf(fatty) != get_turf(src)) - throw_at(get_turf(fatty), 1, 1, spin = FALSE, quickstart = FALSE) + throw_at(get_turf(fatty), range, 1, spin = FALSE, quickstart = FALSE) /** * Causes src to fall onto [target], crushing everything on it (including itself) with [damage] @@ -1263,7 +1260,7 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) var/list/static_record = list( path = replacetext(replacetext("[record.product_path]", "/obj/item/", ""), "/", "-"), name = record.name, - price = premium ? (record.custom_premium_price || extra_price) : (record.custom_price || default_price), + price = record.price, max_amount = record.max_amount, ref = REF(record), ) @@ -1412,9 +1409,7 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) if(!item_record || !istype(item_record) || !item_record.product_path) vend_ready = TRUE return - var/price_to_use = default_price - if(item_record.custom_price) - price_to_use = item_record.custom_price + var/price_to_use = item_record.price if(item_record in hidden_records) if(!extended_inventory) vend_ready = TRUE @@ -1461,20 +1456,16 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) use_energy(active_power_usage) if(icon_vend) //Show the vending animation if needed flick(icon_vend,src) - playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3) var/obj/item/vended_item if(!LAZYLEN(item_record.returned_products)) //always give out free returned stuff first, e.g. to avoid walling a traitor objective in a bag behind paid items - vended_item = new item_record.product_path(get_turf(src)) - if(vended_item.type in contraband) - ADD_TRAIT(vended_item, TRAIT_CONTRABAND, INNATE_TRAIT) - on_dispense(vended_item) + vended_item = dispense(item_record, get_turf(src)) else + playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3) vended_item = LAZYACCESS(item_record.returned_products, LAZYLEN(item_record.returned_products)) //first in, last out LAZYREMOVE(item_record.returned_products, vended_item) vended_item.forceMove(get_turf(src)) if(greyscale_colors) vended_item.set_greyscale(colors=greyscale_colors) - item_record.amount-- if(usr.CanReach(src) && usr.put_in_hands(vended_item)) to_chat(usr, span_notice("You take [item_record.name] out of the slot.")) else @@ -1482,6 +1473,18 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) SSblackbox.record_feedback("nested tally", "vending_machine_usage", 1, list("[type]", "[item_record.product_path]")) vend_ready = TRUE +///Common proc that dispenses an item. Called when the item is vended, or gotten some other way. +/obj/machinery/vending/proc/dispense(datum/data/vending_product/item_record, atom/spawn_location, silent = FALSE) + SHOULD_CALL_PARENT(TRUE) + if(!silent) + playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3) + var/obj/item/vended_item = new item_record.product_path (spawn_location) + if(vended_item.type in contraband) + ADD_TRAIT(vended_item, TRAIT_CONTRABAND, INNATE_TRAIT) + on_dispense(vended_item) + item_record.amount-- + return vended_item + ///A proc meant to perform custom behavior on newly dispensed items. /obj/machinery/vending/proc/on_dispense(obj/item/vended_item) return @@ -1510,8 +1513,6 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) var/datum/bank_account/account = paying_id_card.registered_account if(account.account_job && account.account_job.paycheck_department == payment_department) price_to_use = max(round(price_to_use * DEPARTMENT_DISCOUNT), 1) //No longer free, but signifigantly cheaper. - if(coin_records.Find(product_to_vend) || hidden_records.Find(product_to_vend)) - price_to_use = product_to_vend.custom_premium_price ? product_to_vend.custom_premium_price : extra_price if(LAZYLEN(product_to_vend.returned_products)) price_to_use = 0 //returned items are free if(price_to_use && (attempt_charge(src, mob_paying, price_to_use) & COMPONENT_OBJ_CANCEL_CHARGE)) @@ -1525,7 +1526,7 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) SSblackbox.record_feedback("amount", "vending_spent", price_to_use) SSeconomy.track_purchase(account, price_to_use, name) log_econ("[price_to_use] credits were inserted into [src] by [account.account_holder] to buy [product_to_vend].") - credits_contained += round(price_to_use * 0.2) + credits_contained += round(price_to_use * VENDING_CREDITS_COLLECTION_AMOUNT) return TRUE /obj/machinery/vending/process(seconds_per_tick) @@ -1710,6 +1711,7 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock) payment_department = NO_FREEBIES light_mask = "custom-light-mask" refill_canister = /obj/item/vending_refill/custom + fish_source_path = /datum/fish_source/vending/custom /// where the money is sent var/datum/bank_account/linked_account /// max number of items that the custom vendor can hold diff --git a/code/modules/vending/sustenance.dm b/code/modules/vending/sustenance.dm index 5b54b6bbca4..bc871332c2b 100644 --- a/code/modules/vending/sustenance.dm +++ b/code/modules/vending/sustenance.dm @@ -53,8 +53,6 @@ speak("I don't take bribes! Pay with labor points!") return FALSE var/obj/item/card/id/advanced/prisoner/paying_scum_id = paying_id_card - if(coin_records.Find(product_to_vend) || hidden_records.Find(product_to_vend)) - price_to_use = product_to_vend.custom_premium_price ? product_to_vend.custom_premium_price : extra_price if(LAZYLEN(product_to_vend.returned_products)) price_to_use = 0 //returned items are free if(price_to_use && !(paying_scum_id.points >= price_to_use)) //not enough good prisoner points diff --git a/icons/hud/radial_fishing.dmi b/icons/hud/radial_fishing.dmi index daa4ce8abda3e0cb0b16b8a0252808e20c8c390a..89427cd5db18d40a17317b1d23cb3740bdcbb50b 100644 GIT binary patch literal 11006 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 Zshw-juVyJ>=)G1zUiyPnm4r#qe*s-%&tCul literal 9928 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 diff --git a/strings/fishing_tips.txt b/strings/fishing_tips.txt index fd2e0dccc81..3ce0a252c46 100644 --- a/strings/fishing_tips.txt +++ b/strings/fishing_tips.txt @@ -55,5 +55,6 @@ Feeding a fish mutagen can triple the probability of generating evolved offsprin You can print fishing rods of different materials from an autolathe, which can inrease or decrease fishing difficulty, casting range, experience gained and can have other, special effects. Albeit scarcely, it's possible to catch fish made of the same materials of a custom material fishing rod. Equipping a shiny fishing hook and the quality of the bait can improve your odds. You can use a fishing rod to snatch random organs during the "manipulate organs" step of the "organ manipulation" surgery. +You can try to save cash on vending machines by using a fishing rod with a coin, holochip or bill attached to it. The value of the money used as bait will positively influence difficulty, risks and the odds of getting more expensive items. By opening the aquarium panel and turning "Safe Mode" on, you can easily set up a purely decorative aquarium without having to worry about food, temperature and type of water. Aquariums are also potential fishing spots. Only useful for catching fish you couldn't find in the wild, as a personal achievement and nothing more. \ No newline at end of file