diff --git a/code/__DEFINES/fish.dm b/code/__DEFINES/fish.dm index 04557b4a8e6..b8154bd2d89 100644 --- a/code/__DEFINES/fish.dm +++ b/code/__DEFINES/fish.dm @@ -158,8 +158,8 @@ #define AQUARIUM_FLUID_SALTWATER "Saltwater" #define AQUARIUM_FLUID_SULPHWATEVER "Sulfuric Water" #define AQUARIUM_FLUID_AIR "Air" -#define AQUARIUM_FLUID_ANADROMOUS "Adaptive to both Freshwater and Saltwater" -#define AQUARIUM_FLUID_ANY_WATER "Adaptive to all kind of water" +#define AQUARIUM_FLUID_ANADROMOUS "Anadromous" +#define AQUARIUM_FLUID_ANY_WATER "Any Fluid" ///Fluff. The name of the aquarium company shown in the fish catalog #define AQUARIUM_COMPANY "Aquatech Ltd." @@ -189,6 +189,8 @@ #define FISH_PROPERTIES_FAV_BAIT "fav_bait" #define FISH_PROPERTIES_BAD_BAIT "bad_bait" #define FISH_PROPERTIES_TRAITS "fish_traits" +#define FISH_PROPERTIES_BEAUTY_SCORE "beauty_score" +#define FISH_PROPERTIES_EVOLUTIONS "evolutions" ///Define for favorite and disliked baits that aren't just item typepaths. #define FISH_BAIT_TYPE "Type" @@ -196,3 +198,27 @@ #define FISH_BAIT_REAGENT "Reagent" #define FISH_BAIT_VALUE "Value" #define FISH_BAIT_AMOUNT "Amount" + + +///We multiply the weight of fish inside the loot table by this value if we are goofy enough to fish without a bait. +#define FISH_WEIGHT_MULT_WITHOUT_BAIT 0.15 + +/** + * A macro to ensure the wikimedia filenames of fish icons are unique, especially since there're a couple fish that have + * quite ambiguous names/icon_states like "checkered" or "pike" + */ +#define FISH_AUTOWIKI_FILENAME(fish) SANITIZE_FILENAME("[initial(fish.icon_state)]_wiki_fish") + +///The list keys for the autowiki for fish sources +#define FISH_SOURCE_AUTOWIKI_NAME "name" +#define FISH_SOURCE_AUTOWIKI_ICON "icon" +#define FISH_SOURCE_AUTOWIKI_WEIGHT "weight" +#define FISH_SOURCE_AUTOWIKI_WEIGHT_SUFFIX "weight_suffix" +#define FISH_SOURCE_AUTOWIKI_NOTES "notes" + +///Special value for the name key that always comes first when the data is sorted, regardless of weight. +#define FISH_SOURCE_AUTOWIKI_DUD "Nothing" +///Special value for the name key that always comes last +#define FISH_SOURCE_AUTOWIKI_OTHER "Other Stuff" +///The filename for the icon for "other stuff" which we don't articulate about on the autowiki +#define FISH_SOURCE_AUTOWIKI_QUESTIONMARK "questionmark" diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index f9957f4c739..76651964e24 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -195,3 +195,20 @@ /proc/cmp_deathmatch_mods(datum/deathmatch_modifier/a, datum/deathmatch_modifier/b) return sorttext(b.name, a.name) + +/** + * Orders fish types following this order (freshwater -> saltwater -> anadromous -> sulphuric water -> any water -> air) + * If both share the same required fluid type, they'll be ordered by name instead. + */ +/proc/cmp_fish_fluid(obj/item/fish/a, obj/item/fish/b) + var/static/list/fluids_priority = list( + AQUARIUM_FLUID_FRESHWATER, + AQUARIUM_FLUID_SALTWATER, + AQUARIUM_FLUID_ANADROMOUS, + AQUARIUM_FLUID_SULPHWATEVER, + AQUARIUM_FLUID_ANY_WATER, + AQUARIUM_FLUID_AIR, + ) + var/position_a = fluids_priority.Find(initial(a.required_fluid_type)) + 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)) diff --git a/code/controllers/subsystem/processing/fishing.dm b/code/controllers/subsystem/processing/fishing.dm index a81ff0dec67..0e8c126fe93 100644 --- a/code/controllers/subsystem/processing/fishing.dm +++ b/code/controllers/subsystem/processing/fishing.dm @@ -13,10 +13,38 @@ PROCESSING_SUBSYSTEM_DEF(fishing) fish_properties = list() for(var/fish_type in subtypesof(/obj/item/fish)) var/obj/item/fish/fish = new fish_type(null, FALSE) - fish_properties[fish_type] = list() - fish_properties[fish_type][FISH_PROPERTIES_FAV_BAIT] = fish.favorite_bait.Copy() - fish_properties[fish_type][FISH_PROPERTIES_BAD_BAIT] = fish.disliked_bait.Copy() - fish_properties[fish_type][FISH_PROPERTIES_TRAITS] = fish.fish_traits.Copy() + var/list/properties = list() + fish_properties[fish_type] = properties + properties[FISH_PROPERTIES_FAV_BAIT] = fish.favorite_bait.Copy() + properties[FISH_PROPERTIES_BAD_BAIT] = fish.disliked_bait.Copy() + properties[FISH_PROPERTIES_TRAITS] = fish.fish_traits.Copy() + + var/list/evo_types = fish.evolution_types?.Copy() + properties[FISH_PROPERTIES_EVOLUTIONS] = evo_types + for(var/type in evo_types) + LAZYADD(GLOB.fishes_by_fish_evolution[type], fish_type) + + var/beauty_score = "???" + switch(fish.beauty) + if(-INFINITY to FISH_BEAUTY_DISGUSTING) + beauty_score = "OH HELL NAW!" + if(FISH_BEAUTY_DISGUSTING to FISH_BEAUTY_UGLY) + beauty_score = "☆☆☆☆☆" + if(FISH_BEAUTY_UGLY to FISH_BEAUTY_BAD) + beauty_score = "★☆☆☆☆" + if(FISH_BEAUTY_BAD to FISH_BEAUTY_NULL) + beauty_score = "★★☆☆☆" + if(FISH_BEAUTY_NULL to FISH_BEAUTY_GENERIC) + beauty_score = "★★★☆☆" + if(FISH_BEAUTY_GENERIC to FISH_BEAUTY_GOOD) + beauty_score = "★★★★☆" + if(FISH_BEAUTY_GOOD to FISH_BEAUTY_GREAT) + beauty_score = "★★★★★" + if(FISH_BEAUTY_GREAT to INFINITY) + beauty_score = "★★★★★★" + + properties[FISH_PROPERTIES_BEAUTY_SCORE] = beauty_score + qdel(fish) ///init the list of things lures can catch diff --git a/code/datums/components/profound_fisher.dm b/code/datums/components/profound_fisher.dm index 5bd5af12943..3766d32b14a 100644 --- a/code/datums/components/profound_fisher.dm +++ b/code/datums/components/profound_fisher.dm @@ -127,3 +127,5 @@ line = /obj/item/fishing_line/reinforced bait = /obj/item/food/bait/doughball/synthetic/unconsumable resistance_flags = INDESTRUCTIBLE + reel_overlay = null + show_in_wiki = FALSE //abstract fishing rod diff --git a/code/game/objects/items/food/bait.dm b/code/game/objects/items/food/bait.dm index f31eb44f308..711c6cb1e68 100644 --- a/code/game/objects/items/food/bait.dm +++ b/code/game/objects/items/food/bait.dm @@ -6,6 +6,8 @@ var/bait_quality = TRAIT_BASIC_QUALITY_BAIT /// Icon state added to main fishing rod icon when this bait is equipped var/rod_overlay_icon_state + /// Is this included in the autowiki? + var/show_on_wiki = TRUE /obj/item/food/bait/Initialize(mapload) . = ..() @@ -36,9 +38,14 @@ lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' righthand_file = 'icons/mob/inhands/items_righthand.dmi' inhand_icon_state = "pen" + bait_quality = TRAIT_GREAT_QUALITY_BAIT //this is only here for autowiki purposes, it's removed on init. food_reagents = list(/datum/reagent/drug/kronkaine = 2) //The kronkaine is the thing that makes this a great bait. tastes = list("hypocrisy" = 1) +/obj/item/food/bait/natural/Initialize(mapload) + . = ..() + REMOVE_TRAIT(src, bait_quality, INNATE_TRAIT) + /obj/item/food/bait/doughball name = "doughball" desc = "Small piece of dough. Simple but effective fishing bait." @@ -51,17 +58,12 @@ bait_quality = TRAIT_BASIC_QUALITY_BAIT rod_overlay_icon_state = "dough_overlay" -/** - * Bound to the tech fishing rod, from which cannot be removed, - * Bait-related preferences and traits, both negative and positive, - * should be ignored by this bait. - * Otherwise it'd be hard/impossible to cath some fish with it, - * making that rod a shoddy choice in the long run. - */ +///The abstract synthetic doughball type. /obj/item/food/bait/doughball/synthetic name = "synthetic doughball" icon_state = "doughball_blue" preserved_food = TRUE + show_on_wiki = FALSE //It's an abstract item. /obj/item/food/bait/doughball/synthetic/Initialize(mapload) . = ..() @@ -70,10 +72,17 @@ ///Found in the can of omni-baits, only available from the super fishing toolbox, from the fishing mystery box. /obj/item/food/bait/doughball/synthetic/super name = "super-doughball" - desc = "No fish will be able to resist this." + desc = "Be they herbivore or carnivores, no fish will be able to resist this." bait_quality = TRAIT_GREAT_QUALITY_BAIT + show_on_wiki = TRUE -///Used by the advanced fishing rod +/** + * Bound to the tech fishing rod, from which cannot be removed, + * Bait-related preferences and traits, both negative and positive, + * should be ignored by this bait. + * Otherwise it'd be hard/impossible to cath some fish with it, + * making that rod a shoddy choice in the long run. + */ /obj/item/food/bait/doughball/syntethic/unconsumable /obj/item/food/bait/doughball/synthetic/unconsumable/Initialize(mapload) diff --git a/code/modules/autowiki/pages/base.dm b/code/modules/autowiki/pages/base.dm index 8e745ace61c..ce32bfd4032 100644 --- a/code/modules/autowiki/pages/base.dm +++ b/code/modules/autowiki/pages/base.dm @@ -46,7 +46,12 @@ if (IsAdminAdvancedProcCall()) return + var/static/uploaded_icons = list() + if(uploaded_icons["[name]"]) + CRASH("We tried uploading an icon, but the name \"[name]\" was already taken!") + fcopy(icon, "data/autowiki_files/[name].png") + uploaded_icons["[name]"] = TRUE /// Escape a parameter such that it can be correctly put inside a wiki output /datum/autowiki/proc/escape_value(parameter) diff --git a/code/modules/autowiki/pages/fishing.dm b/code/modules/autowiki/pages/fishing.dm new file mode 100644 index 00000000000..1267547c85c --- /dev/null +++ b/code/modules/autowiki/pages/fishing.dm @@ -0,0 +1,445 @@ +/datum/autowiki/fish + page = "Template:Autowiki/Content/Fish" + +/datum/autowiki/fish/generate() + var/output = "" + + var/datum/reagent/def_food = /obj/item/fish::food + var/def_food_name = initial(def_food.name) + var/def_feeding = /obj/item/fish::feeding_frequency + var/def_feeding_text = DisplayTimeText(def_feeding) + var/def_breeding = /obj/item/fish::breeding_timeout + var/def_breeding_text = DisplayTimeText(def_breeding) + + var/list/generated_icons = list() + var/list/fish_types = subtypesof(/obj/item/fish) + sortTim(fish_types, GLOBAL_PROC_REF(cmp_fish_fluid)) + + for (var/obj/item/fish/fish as anything in fish_types) + + var/filename = FISH_AUTOWIKI_FILENAME(fish) + + if(!generated_icons[filename]) + upload_icon(icon(fish:icon, fish::icon_state, frame = 1), filename) + generated_icons[filename] = TRUE + + if(!fish::show_in_catalog) + continue + + var/list/properties = SSfishing.fish_properties[fish] + + var/description = escape_value(fish::desc) + var/list/extra_info = list() + if(fish::fillet_type != /obj/item/food/fishmeat) + var/obj/item/fillet = fish::fillet_type + if(!fillet) + extra_info += "Cannot be butchered." + else + extra_info += "When butchered, it'll yield [initial(fillet.name)]." + var/datum/reagent/food = fish::food + if(food != def_food) + extra_info += "It has to be fed [initial(food.name)] instead of [def_food_name]" + if(fish::feeding_frequency != def_feeding) + extra_info += "It has to be fed every [DisplayTimeText(fish::feeding_frequency)] instead of [def_feeding_text]" + if(fish::breeding_timeout != def_breeding) + extra_info += "It takes [DisplayTimeText(fish::breeding_timeout)] to reproduce instead of [def_breeding_text]" + if(length(extra_info)) + description += "
[extra_info.Join(extra_info,"
")]" + + output += "\n\n" + include_template("Autowiki/FishEntry", list( + "name" = full_capitalize(escape_value(fish::name)), + "icon" = filename, + "description" = description, + "size_weight" = "[fish::average_size]cm / [fish::average_weight]g", + "fluid" = escape_value(fish::required_fluid_type), + "temperature" = "[fish::required_temperature_min] - [fish::required_temperature_max] K", + "stable_population" = fish::stable_population, + "traits" = generate_traits(properties[FISH_PROPERTIES_TRAITS]), + "favorite_baits" = generate_baits(properties[FISH_PROPERTIES_FAV_BAIT]), + "disliked_baits" = generate_baits(properties[FISH_PROPERTIES_BAD_BAIT], TRUE), + "beauty_score" = properties[FISH_PROPERTIES_BEAUTY_SCORE], + )) + + return output + +/datum/autowiki/fish/proc/generate_baits(list/baits, bad = FALSE) + var/list/list = list() + if(!length(baits)) + return list("None") + + for (var/identifier in baits) + if(ispath(identifier)) //Just a path + var/obj/item/item = identifier + list += initial(item.name) + continue + var/list/special_identifier = identifier + switch(special_identifier["Type"]) + if("Foodtype") + list += english_list(bitfield_to_list(special_identifier["Value"], FOOD_FLAGS_IC)) + if("Reagent") + var/datum/reagent/reagent = special_identifier["Value"] + list += "[reagent::name][bad ? "" : "(At least [special_identifier["Amount"]] units)"]" + + return list + +/datum/autowiki/fish/proc/generate_traits(list/traits) + var/output = "" + + for(var/trait_type in traits) + var/datum/fish_trait/trait = GLOB.fish_traits[trait_type] + output += include_template("Autowiki/FishTypeTraits", list( + "name" = escape_value(trait.name), + "description" = escape_value(trait.catalog_description), + )) + + return output + +/datum/autowiki/fish_trait + page = "Template:Autowiki/Content/Fish/Trait" + +/datum/autowiki/fish_trait/generate() + var/output = "" + + for(var/trait_type in GLOB.fish_traits) + var/datum/fish_trait/trait = GLOB.fish_traits[trait_type] + var/desc = escape_value(trait.catalog_description) + if(length(trait.incompatible_traits)) + var/incompatible = list() + for(var/datum/fish_trait/bad as anything in trait.incompatible_traits) + incompatible += span_bold(initial(bad.name)) + desc += "
Incompatible with [english_list(incompatible)]." + output += include_template("Autowiki/FishAllTraits", list( + "name" = escape_value(trait.name), + "description" = escape_value(trait.catalog_description), + "inheritability" = trait.inheritability, + "inheritability_diff" = trait.diff_traits_inheritability, + + )) + + return output + +/datum/autowiki/fish_bait + page = "Template:Autowiki/Content/Fish/Bait" + +/datum/autowiki/fish_bait/generate() + var/output = "" + + var/list/generated_icons = list() + for (var/obj/item/food/bait/bait as anything in subtypesof(/obj/item/food/bait)) + if(!bait::show_on_wiki) + continue + + var/filename = SANITIZE_FILENAME("[bait::icon_state]_wiki_bait") + + var/quality = "Bland" + + var/list/foodtypes + if(ispath(bait, /obj/item/food/bait/doughball/synthetic)) + foodtypes = list("Don't worry about it") + else + foodtypes = bitfield_to_list(bait::foodtypes, FOOD_FLAGS_IC) || list("None") + + switch(bait::bait_quality) + if(TRAIT_BASIC_QUALITY_BAIT) + quality = "Basic" + if(TRAIT_GOOD_QUALITY_BAIT) + quality = "Good" + if(TRAIT_GREAT_QUALITY_BAIT) + quality = "Great" + + output += "\n\n" + include_template("Autowiki/FishBait", list( + "name" = full_capitalize(escape_value(bait::name)), + "icon" = filename, + "description" = escape_value(bait::desc), + "foodtypes" = foodtypes, + "quality" = quality, + )) + + if(!generated_icons[filename]) + upload_icon(icon(bait:icon, bait::icon_state, frame = 1), filename) + generated_icons[filename] = TRUE + + var/filename = SANITIZE_FILENAME(/obj/item/stock_parts/power_store/cell/lead::icon_state) + + var/lead_desc = /obj/item/stock_parts/power_store/cell/lead::desc + lead_desc += " You probably shouldn't use it unless you're trying to catch a zipzap." + output += "\n\n" + include_template("Autowiki/FishBait", list( + "name" = full_capitalize(escape_value(/obj/item/stock_parts/power_store/cell/lead::name)), + "icon" = filename, + "description" = lead_desc, + "foodtypes" = "None", + "quality" = "Poisonous", + )) + + upload_icon(icon(/obj/item/stock_parts/power_store/cell/lead::icon, /obj/item/stock_parts/power_store/cell/lead::icon_state), filename) + + var/obj/needletype = /obj/item/fish/needlefish + output += "\n\n" + include_template("Autowiki/FishBait", list( + "name" = "Baitfish", + "icon" = FISH_AUTOWIKI_FILENAME(needletype), + "description" = "Smaller fish such as goldfish, needlefish, armorfish and lavaloops can also be used as bait, It's a fish eat fish world.", + "foodtypes" = "Seafood?", + "quality" = "Good", + )) + + output += "\n\n" + include_template("Autowiki/FishBait", list( + "name" = "Food", + "icon" = "plain_bread", + "description" = "In absence of baits, food can be used as a substitute.", + "foodtypes" = "Depends", + "quality" = "Bland most of the times", + )) + + upload_icon(icon(/obj/item/food/bread/plain::icon, /obj/item/food/bread/plain::icon_state), "plain_bread") + + return output + +/datum/autowiki/fishing_line + page = "Template:Autowiki/Content/Fish/Line" + +/datum/autowiki/fishing_line/generate() + var/output = "" + + var/list/generated_icons = list() + for (var/obj/item/fishing_line/line as anything in typesof(/obj/item/fishing_line)) + var/filename = SANITIZE_FILENAME("[line::icon_state]_wiki_line") + + output += "\n\n" + include_template("Autowiki/FishLine", list( + "name" = full_capitalize(escape_value(line::name)), + "icon" = filename, + "description" = escape_value(line::wiki_desc), + )) + + if(!generated_icons[filename]) + upload_icon(icon(line:icon, line::icon_state), filename) + generated_icons[filename] = TRUE + + return output + +/datum/autowiki/fishing_hook + page = "Template:Autowiki/Content/Fish/Hook" + +/datum/autowiki/fishing_hook/generate() + var/output = "" + + var/list/generated_icons = list() + for (var/obj/item/fishing_hook/hook as anything in typesof(/obj/item/fishing_hook)) + var/filename = SANITIZE_FILENAME("[hook::icon_state]_wiki_hook") + + output += "\n\n" + include_template("Autowiki/FishHook", list( + "name" = full_capitalize(escape_value(hook::name)), + "icon" = filename, + "description" = escape_value(hook::wiki_desc), + )) + + if(!generated_icons[filename]) + upload_icon(icon(hook:icon, hook::icon_state), filename) + generated_icons[filename] = TRUE + + return output + +/datum/autowiki/fishing_rod + page = "Template:Autowiki/Content/Fish/Rod" + +/datum/autowiki/fishing_rod/generate() + var/output = "" + + var/list/generated_icons = list() + for (var/obj/item/fishing_rod/rod as anything in typesof(/obj/item/fishing_rod)) + if(!rod::show_in_wiki) + continue + + var/filename = SANITIZE_FILENAME("[rod::icon_state]_wiki_rod") + + var/desc = escape_value(rod::ui_description) + if(rod::wiki_description) + desc += "
[escape_value(rod::wiki_description)]" + output += "\n\n" + include_template("Autowiki/FishingRod", list( + "name" = full_capitalize(escape_value(rod::name)), + "icon" = filename, + "description" = desc, + )) + + if(!generated_icons[filename]) + var/icon/rod_icon = icon(rod:icon, rod::icon_state) + if(rod::reel_overlay) + var/icon/line = icon(rod::icon, rod::reel_overlay) + line.Blend(rod::default_line_color, ICON_MULTIPLY) + rod_icon.Blend(line, ICON_OVERLAY) + upload_icon(rod_icon, filename) + generated_icons[filename] = TRUE + + return output + +/datum/autowiki/fish_sources + page = "Template:Autowiki/Content/Fish/Source" + +/datum/autowiki/fish_sources/generate() + var/output = "" + + for(var/source_type in GLOB.preset_fish_sources) + var/datum/fish_source/source = GLOB.preset_fish_sources[source_type] + if(!source.catalog_description) + continue + + output += "\n\n" + include_template("Autowiki/FishSource", list( + "name" = full_capitalize(source.catalog_description), + "difficulty" = source.fishing_difficulty, + "contents" = get_contents(source), + )) + + ///Used for stuff that isn't fish by default + upload_icon(icon('icons/effects/random_spawners.dmi', "questionmark"), FISH_SOURCE_AUTOWIKI_QUESTIONMARK) + + return output + +/datum/autowiki/fish_sources/proc/get_contents(datum/fish_source/source) + var/output = "" + var/list/data = source.generate_wiki_contents(src) + sortTim(data, GLOBAL_PROC_REF(cmp_autowiki_fish_sources_content)) + for(var/list/entry in data) + entry[FISH_SOURCE_AUTOWIKI_WEIGHT] = "[round(entry[FISH_SOURCE_AUTOWIKI_WEIGHT], 0.1)]%" + var/weight_suffix = entry[FISH_SOURCE_AUTOWIKI_WEIGHT_SUFFIX] + if(weight_suffix) + entry[FISH_SOURCE_AUTOWIKI_WEIGHT] += " [weight_suffix]" + entry -= FISH_SOURCE_AUTOWIKI_WEIGHT + output += include_template("Autowiki/FishSourceContents", entry) + + return output + +///Sort the autowiki fish entries by their weight. However, duds always come first. +/proc/cmp_autowiki_fish_sources_content(list/A, list/B) + if(A[FISH_SOURCE_AUTOWIKI_NAME] == FISH_SOURCE_AUTOWIKI_DUD) + return -1 + if(B[FISH_SOURCE_AUTOWIKI_NAME] == FISH_SOURCE_AUTOWIKI_DUD) + return 1 + if(A[FISH_SOURCE_AUTOWIKI_NAME] == FISH_SOURCE_AUTOWIKI_OTHER) + return 1 + if(B[FISH_SOURCE_AUTOWIKI_NAME] == FISH_SOURCE_AUTOWIKI_OTHER) + return -1 + return B[FISH_SOURCE_AUTOWIKI_WEIGHT] - A[FISH_SOURCE_AUTOWIKI_WEIGHT] + +/datum/autowiki/fish_scan + page = "Template:Autowiki/Content/Fish/Scan" + +/datum/autowiki/fish_scan/generate() + var/output = "" + + var/list/generated_icons = list() + var/datum/techweb/techweb = locate(/datum/techweb/admin) in SSresearch.techwebs + for(var/scan_type in typesof(/datum/experiment/scanning/fish)) + techweb.add_experiment(scan_type) //Make sure each followup experiment is available + var/datum/experiment/scanning/fish/scan = locate(scan_type) in techweb.available_experiments + if(!scan) //Just to be sure, if the scan was already completed. + scan = locate(scan_type) in techweb.completed_experiments + + output += "\n\n" + include_template("Autowiki/FishScan", list( + "name" = full_capitalize(escape_value(scan.name)), + "description" = escape_value(scan.description), + "requirements" = build_requirements(scan), + "rewards" = build_rewards(scan, generated_icons), + + )) + + return output + +/datum/autowiki/fish_scan/proc/build_requirements(datum/experiment/scanning/fish/scan) + var/output = "" + for(var/obj/item/type as anything in scan.required_atoms) + var/name = initial(type.name) + //snowflake case because the default holographic fish is called goldfish but we don't want to confuse readers. + if(type == /obj/item/fish/holo) + name = "holographic Fish" + output += include_template("Autowiki/FishScanRequirements", list( + "name" = full_capitalize(escape_value(name)), + "amount" = scan.required_atoms[type], + )) + return output + +/datum/autowiki/fish_scan/proc/build_rewards(datum/experiment/scanning/fish/scan, list/generated_icons) + var/output = "" + var/datum/fish_source/portal/reward = GLOB.preset_fish_sources[scan.fish_source_reward] + var/filename = SANITIZE_FILENAME("fishing_portal_[reward.radial_state]") + + var/list/unlocks = list() + for(var/datum/experiment/unlock as anything in scan.next_experiments) + unlocks += initial(unlock.name) + output += include_template("Autowiki/FishScanRewards", list( + "name" = full_capitalize(escape_value("[reward.radial_name] Dimension")), + "icon" = filename, + "points" = scan.get_points_reward_text(), + "unlock" = english_list(unlocks, nothing_text = "Nothing"), + )) + + if(!generated_icons[filename]) + upload_icon(icon(icon = 'icons/hud/radial_fishing.dmi', icon_state = reward.radial_state), filename) + generated_icons[filename] = TRUE + + return output + +/datum/autowiki/fish_evolution + page = "Template:Autowiki/Content/Fish/Evolution" + +/datum/autowiki/fish_evolution/generate() + var/output = "" + + for(var/evo_type in GLOB.fish_evolutions) + var/datum/fish_evolution/evolution = GLOB.fish_evolutions[evo_type] + if(!evolution.show_on_wiki) + continue + + output += "\n\n" + include_template("Autowiki/FishEvolution", list( + "name" = escape_value(evolution.name), + "fish" = get_fish(evo_type), + "min_max_temp" = "[evolution.required_temperature_min] - [evolution.required_temperature_max] K", + "notes" = escape_value(evolution.conditions_note), + "result_icon" = evolution.show_result_on_wiki ? FISH_AUTOWIKI_FILENAME(evolution.new_fish_type) : FISH_SOURCE_AUTOWIKI_QUESTIONMARK, + )) + + return output + +/datum/autowiki/fish_evolution/proc/get_fish(evo_type) + var/output = "" + + for(var/obj/item/fish/fish as anything in GLOB.fishes_by_fish_evolution[evo_type]) + if(!initial(fish.show_in_catalog)) + continue + output += include_template("Autowiki/FishEvolutionCandidate", list( + "name" = escape_value(full_capitalize(initial(fish.name))), + "icon" = FISH_AUTOWIKI_FILENAME(fish), + )) + + return output + +/datum/autowiki/fish_lure + page = "Template:Autowiki/Content/Fish/Lure" + +/datum/autowiki/fish_lure/generate() + var/output = "" + + for(var/obj/item/fishing_lure/lure as anything in SSfishing.lure_catchables) + var/state = initial(lure.icon_state) + var/filename = SANITIZE_FILENAME("[state]_wiki_lure") + output += "\n\n" + include_template("Autowiki/FishLure", list( + "name" = escape_value(full_capitalize(initial(lure.name))), + "desc" = escape_value(initial(lure.name)), + "icon" = filename, + "catchables" = build_catchables(SSfishing.lure_catchables[lure]), + )) + + upload_icon(icon(icon = initial(lure.icon), icon_state = state), filename) + + return output + +/datum/autowiki/fish_lure/proc/build_catchables(list/catchables) + var/output = "" + + for(var/obj/item/fish/fish as anything in catchables) + if(!initial(fish.show_in_catalog)) + continue + output += include_template("Autowiki/FishLureCatchables", list( + "name" = escape_value(full_capitalize(initial(fish.name))), + "icon" = FISH_AUTOWIKI_FILENAME(fish), + )) + + return output diff --git a/code/modules/autowiki/pages/soup.dm b/code/modules/autowiki/pages/soup.dm index f67d00e97a0..754beb3a82a 100644 --- a/code/modules/autowiki/pages/soup.dm +++ b/code/modules/autowiki/pages/soup.dm @@ -16,6 +16,7 @@ var/container_for_images = /obj/item/reagent_containers/cup/bowl + var/list/already_generated_icons = list() for(var/soup_recipe_type in subtypesof(/datum/chemical_reaction/food/soup)) var/datum/chemical_reaction/food/soup/soup_recipe = new soup_recipe_type() // Used to determine what icon is displayed on the wiki @@ -123,14 +124,17 @@ template_list["results"] = escape_value(compiled_results) // -- While we're here, generate an icon of the bowl -- - if(!soup_icon_state) - var/obj/item/reagent_containers/cup/bowl/soup_bowl = new() - soup_bowl.reagents.add_reagent(result_soup_type, soup_bowl.reagents.maximum_volume) - upload_icon(getFlatIcon(soup_bowl, no_anim = TRUE), filename) - qdel(soup_bowl) - else - var/image/compiled_image = image(icon = soup_icon, icon_state = soup_icon_state) - upload_icon(getFlatIcon(compiled_image, no_anim = TRUE), filename) + + if(!already_generated_icons[filename]) + if(!soup_icon_state) + var/obj/item/reagent_containers/cup/bowl/soup_bowl = new() + soup_bowl.reagents.add_reagent(result_soup_type, soup_bowl.reagents.maximum_volume) + upload_icon(getFlatIcon(soup_bowl, no_anim = TRUE), filename) + qdel(soup_bowl) + else + var/image/compiled_image = image(icon = soup_icon, icon_state = soup_icon_state) + upload_icon(getFlatIcon(compiled_image, no_anim = TRUE), filename) + already_generated_icons[filename] = TRUE // -- Cleanup -- qdel(soup_recipe) diff --git a/code/modules/autowiki/pages/vending.dm b/code/modules/autowiki/pages/vending.dm index 0a8dd3db0a9..e110afa760e 100644 --- a/code/modules/autowiki/pages/vending.dm +++ b/code/modules/autowiki/pages/vending.dm @@ -10,7 +10,10 @@ // So we put it inside, something var/obj/parent = new - for (var/vending_type in sort_list(subtypesof(/obj/machinery/vending), GLOBAL_PROC_REF(cmp_typepaths_asc))) + for (var/obj/machinery/vending/vending_type as anything in sort_list(subtypesof(/obj/machinery/vending), GLOBAL_PROC_REF(cmp_typepaths_asc))) + var/obj/machinery/vending/parent_machine = type2parent(vending_type) + if(initial(parent_machine.name) == initial(vending_type.name)) + continue //Same name, likely just a slightly touched up subtype for specific maps. var/obj/machinery/vending/vending_machine = new vending_type(parent) vending_machine.use_power = FALSE vending_machine.update_icon(UPDATE_ICON_STATE) diff --git a/code/modules/clothing/gloves/special.dm b/code/modules/clothing/gloves/special.dm index 1366a29ac4a..b5c16ba4dc5 100644 --- a/code/modules/clothing/gloves/special.dm +++ b/code/modules/clothing/gloves/special.dm @@ -210,11 +210,14 @@ ///The internal fishing rod of the athletic fishing gloves. The more athletic you're, the easier the minigame will be. /obj/item/fishing_rod/mob_fisher/athletic + name = "athletics fishing gloves" icon = /obj/item/clothing/gloves/fishing::icon icon_state = /obj/item/clothing/gloves/fishing::icon_state line = null bait = null - ui_description = "The integrated fishing rod of a pair of athletic fishing gloves" + ui_description = "A pair of gloves to fish without a fishing rod while training your athletics." + wiki_description = "It requires the Advanced Fishing Technology Node to be researched to be printed. It may hurt the user when catching larger fish." + show_in_wiki = TRUE //Show this cool pair of gloves in the wiki. /obj/item/fishing_rod/mob_fisher/athletic/Initialize(mapload) . = ..() diff --git a/code/modules/experisci/experiment/types/experiment.dm b/code/modules/experisci/experiment/types/experiment.dm index add015622f6..358d795f68f 100644 --- a/code/modules/experisci/experiment/types/experiment.dm +++ b/code/modules/experisci/experiment/types/experiment.dm @@ -99,3 +99,9 @@ experiment_handler.selected_experiment = null var/announcetext = experiment_handler.linked_web.complete_experiment(src) experiment_handler.announce_message_to_all(announcetext) + +/datum/experiment/proc/get_points_reward_text() + var/list/english_list_keys = list() + for(var/points_type in points_reward) + english_list_keys += "[points_reward[points_type]] [points_type]" + return "[english_list(english_list_keys)] points" diff --git a/code/modules/fishing/bait.dm b/code/modules/fishing/bait.dm index 8fb66ad4c3d..fbbe23d2ccc 100644 --- a/code/modules/fishing/bait.dm +++ b/code/modules/fishing/bait.dm @@ -1,6 +1,6 @@ /obj/item/bait_can name = "can o bait" - desc = "there's a lot of them in there, getting them out takes a while though" + desc = "there's a lot of them in there, getting them out takes a while though." icon = 'icons/obj/fishing.dmi' icon_state = "bait_can" base_icon_state = "bait_can" diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm index 08d09188e6b..cdaa1902b09 100644 --- a/code/modules/fishing/fish/_fish.dm +++ b/code/modules/fishing/fish/_fish.dm @@ -6,7 +6,7 @@ // Fish path used for autogenerated fish /obj/item/fish - name = "generic looking aquarium fish" + name = "fish" desc = "very bland" icon = 'icons/obj/aquarium/fish.dmi' lefthand_file = 'icons/mob/inhands/fish_lefthand.dmi' diff --git a/code/modules/fishing/fish/fish_evolution.dm b/code/modules/fishing/fish/fish_evolution.dm index 688b0c201c7..d7b8f744f0a 100644 --- a/code/modules/fishing/fish/fish_evolution.dm +++ b/code/modules/fishing/fish/fish_evolution.dm @@ -1,4 +1,7 @@ +///A global list of fish evolutions, which are singletons. GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolution, list())) +///A list of fish evolution types, each having an associated list containing all fish types that have it. +GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) /** * Fish evolution datums @@ -7,7 +10,9 @@ GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolutio * then there's a chance the offspring may be of a new type rather than the same as its source or mate (if any). */ /datum/fish_evolution + ///The name of the evolution. If not set, it'll be generated on runtime from the name of the new fish type. var/name + ///The probability that this evolution can happen. var/probability = 0 ///The obj/item/fish path of the new fish var/obj/item/fish/new_fish_type = /obj/item/fish @@ -21,8 +26,14 @@ GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolutio var/list/removed_traits ///A text string shown in the catalog, containing information on conditions specific to this evolution. var/conditions_note + ///Is this evolution shown on the wiki? + var/show_on_wiki = TRUE + ///Is the result of this evolution shown on the wiki? + var/show_result_on_wiki = TRUE /datum/fish_evolution/New() + ..() + SHOULD_CALL_PARENT(TRUE) if(!ispath(new_fish_type, /obj/item/fish)) stack_trace("[type] instantiated with a new fish type of [new_fish_type]. That's not a fish, hun, things will break.") if(!name) @@ -87,6 +98,7 @@ GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolutio new_fish_type = /obj/item/fish/mastodon new_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/amphibious, /datum/fish_trait/predator, /datum/fish_trait/aggressive) conditions_note = "The fish (and its mate) needs to be unusually big both in size and weight." + show_result_on_wiki = FALSE /datum/fish_evolution/mastodon/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) if((source.size < 120 || source.weight < 3000) || (mate && (mate.size < 120 || mate.weight < 3000))) @@ -106,13 +118,11 @@ GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolutio required_temperature_max = MIN_AQUARIUM_TEMP+10 /datum/fish_evolution/three_eyes - name = "Three-eyed Goldfish" probability = 3 new_fish_type = /obj/item/fish/goldfish/three_eyes new_traits = list(/datum/fish_trait/recessive) /datum/fish_evolution/chainsawfish - name = "Chainsawfish" probability = 30 new_fish_type = /obj/item/fish/chainsawfish new_traits = list(/datum/fish_trait/predator, /datum/fish_trait/aggressive) diff --git a/code/modules/fishing/fish/fish_traits.dm b/code/modules/fishing/fish/fish_traits.dm index bb52ee095a8..c289e1d8907 100644 --- a/code/modules/fishing/fish/fish_traits.dm +++ b/code/modules/fishing/fish/fish_traits.dm @@ -132,10 +132,16 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/shiny_lover/difficulty_mod(obj/item/fishing_rod/rod, mob/fisherman) . = ..() - // These fish are easier to catch with shiny lure + // These fish are easier to catch with shiny hook if(rod.hook && rod.hook.fishing_hook_traits & FISHING_HOOK_SHINY) .[ADDITIVE_FISHING_MOD] -= FISH_TRAIT_MINOR_DIFFICULTY_BOOST +/datum/fish_trait/shiny_lover/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman) + . = ..() + // These fish are harder to find without a shiny hook + if(rod.hook && rod.hook.fishing_hook_traits & FISHING_HOOK_SHINY) + .[MULTIPLICATIVE_FISHING_MOD] = 0.5 + /datum/fish_trait/picky_eater name = "Picky Eater" catalog_description = "This fish is very picky and will ignore low quality bait (unless it's amongst its favorites)." diff --git a/code/modules/fishing/fish_catalog.dm b/code/modules/fishing/fish_catalog.dm index f0880804f8e..6a64b85184f 100644 --- a/code/modules/fishing/fish_catalog.dm +++ b/code/modules/fishing/fish_catalog.dm @@ -16,11 +16,10 @@ var/static/fish_info if(!fish_info) fish_info = list() - for(var/_fish_type as anything in subtypesof(/obj/item/fish)) - var/obj/item/fish/fish = _fish_type - var/list/fish_data = list() + for(var/obj/item/fish/fish as anything in subtypesof(/obj/item/fish)) if(!initial(fish.show_in_catalog)) continue + var/list/fish_data = list() fish_data["name"] = initial(fish.name) fish_data["desc"] = initial(fish.desc) fish_data["fluid"] = initial(fish.required_fluid_type) @@ -37,27 +36,9 @@ else fish_data["feed"] = "[AQUARIUM_COMPANY] Fish Feed" fish_data["fishing_tips"] = build_fishing_tips(fish) - var/beauty_score = initial(fish.beauty) - switch(beauty_score) - if(-INFINITY to FISH_BEAUTY_DISGUSTING) - beauty_score = "OH HELL NAW!" - if(FISH_BEAUTY_DISGUSTING to FISH_BEAUTY_UGLY) - beauty_score = "☆☆☆☆☆" - if(FISH_BEAUTY_UGLY to FISH_BEAUTY_BAD) - beauty_score = "★☆☆☆☆" - if(FISH_BEAUTY_BAD to FISH_BEAUTY_NULL) - beauty_score = "★★☆☆☆" - if(FISH_BEAUTY_NULL to FISH_BEAUTY_GENERIC) - beauty_score = "★★★☆☆" - if(FISH_BEAUTY_GENERIC to FISH_BEAUTY_GOOD) - beauty_score = "★★★★☆" - if(FISH_BEAUTY_GOOD to FISH_BEAUTY_GREAT) - beauty_score = "★★★★★" - if(FISH_BEAUTY_GREAT to INFINITY) - beauty_score = "★★★★★★" - fish_data["beauty"] = beauty_score + fish_data["beauty"] = SSfishing.fish_properties[fish][FISH_PROPERTIES_BEAUTY_SCORE] + fish_info += list(fish_data) - // TODO: Custom entries for unusual stuff .["fish_info"] = fish_info .["sponsored_by"] = AQUARIUM_COMPANY @@ -68,12 +49,12 @@ return initial(bait_item.name) if(islist(bait)) var/list/special_identifier = bait - switch(special_identifier["Type"]) - if("Foodtype") - return jointext(bitfield_to_list(special_identifier["Value"], FOOD_FLAGS_IC),",") - if("Reagent") - var/datum/reagent/prototype = special_identifier["Value"] - return "[initial(prototype.name)] (at least [special_identifier["Amount"]]u)" + switch(special_identifier[FISH_BAIT_TYPE]) + if(FISH_BAIT_FOODTYPE) + return jointext(bitfield_to_list(special_identifier[FISH_BAIT_VALUE], FOOD_FLAGS_IC),",") + if(FISH_BAIT_REAGENT) + var/datum/reagent/prototype = special_identifier[FISH_BAIT_VALUE] + return "[initial(prototype.name)] (at least [special_identifier[FISH_BAIT_AMOUNT]]u)" else stack_trace("Unknown bait identifier in fish favourite/disliked list") return "SOMETHING VERY WEIRD" @@ -91,8 +72,8 @@ spot_descriptions += source.catalog_description .["spots"] = english_list(spot_descriptions, nothing_text = "Unknown") var/list/fish_list_properties = SSfishing.fish_properties - var/list/fav_bait = fish_list_properties[fishy][NAMEOF(fishy, favorite_bait)] - var/list/disliked_bait = fish_list_properties[fishy][NAMEOF(fishy, disliked_bait)] + var/list/fav_bait = fish_list_properties[fishy][FISH_PROPERTIES_FAV_BAIT] + var/list/disliked_bait = fish_list_properties[fishy][FISH_PROPERTIES_BAD_BAIT] var/list/bait_list = list() // Favourite/Disliked bait for(var/bait_type_or_trait in fav_bait) @@ -104,7 +85,7 @@ .["disliked_bait"] = english_list(bait_list, nothing_text = "None") // Fish traits description var/list/trait_descriptions = list() - var/list/fish_traits = fish_list_properties[fishy][NAMEOF(fishy, fish_traits)] + var/list/fish_traits = fish_list_properties[fishy][FISH_PROPERTIES_TRAITS] var/fish_difficulty = initial(fishy.fishing_difficulty_modifier) for(var/fish_trait in fish_traits) var/datum/fish_trait/trait = GLOB.fish_traits[fish_trait] diff --git a/code/modules/fishing/fishing_equipment.dm b/code/modules/fishing/fishing_equipment.dm index e20f6c54a91..5b2a250f42b 100644 --- a/code/modules/fishing/fishing_equipment.dm +++ b/code/modules/fishing/fishing_equipment.dm @@ -16,6 +16,8 @@ var/list/fishing_line_traits /// Color of the fishing line var/line_color = COLOR_GRAY + ///The description given to the autowiki + var/wiki_desc = "A generic fishing line. Without one, the casting range of the rod will be significantly hampered." /obj/item/fishing_line/reinforced name = "reinforced fishing line reel" @@ -23,6 +25,7 @@ icon_state = "reel_green" fishing_line_traits = FISHING_LINE_REINFORCED line_color = "#2b9c2b" + wiki_desc = "Allows you to fish in lava and plasma rivers and lakes." /obj/item/fishing_line/cloaked name = "cloaked fishing line reel" @@ -30,6 +33,7 @@ icon_state = "reel_white" fishing_line_traits = FISHING_LINE_CLOAKED line_color = "#82cfdd" + wiki_desc = "Fishing anxious and wary fish will be easier with this equipped." /obj/item/fishing_line/bouncy name = "flexible fishing line reel" @@ -37,6 +41,7 @@ icon_state = "reel_red" fishing_line_traits = FISHING_LINE_BOUNCY line_color = "#99313f" + wiki_desc = "It reduces the progression loss during the fishing minigame." /obj/item/fishing_line/sinew name = "fishing sinew" @@ -44,6 +49,7 @@ icon_state = "reel_sinew" fishing_line_traits = FISHING_LINE_REINFORCED|FISHING_LINE_STIFF line_color = "#d1cca3" + wiki_desc = "Crafted from sinew. It allows you to fish in lava and plasma like the reinforced line, but it'll make the minigame harder." /** * A special line reel that let you skip the biting phase of the minigame, netting you a completion bonus, @@ -56,6 +62,9 @@ icon_state = "reel_auto" fishing_line_traits = FISHING_LINE_AUTOREEL line_color = "#F88414" + wiki_desc = "Automatically starts the minigame once the fish bites the bait. It also spin fishing lures for you without needing an input. \ + It can also be used to snag in objects from a distance more rapidly.
\ + It requires the Advanced Fishing Technology Node to be researched to be printed." /obj/item/fishing_line/auto_reel/Initialize(mapload) . = ..() @@ -118,7 +127,8 @@ var/rod_overlay_icon_state = "hook_overlay" /// What subtype of `/obj/item/chasm_detritus` do we fish out of chasms? Defaults to `/obj/item/chasm_detritus`. var/chasm_detritus_type = /datum/chasm_detritus - + ///The description given to the autowiki + var/wiki_desc = "A generic fishing hook. You won't be able to fish without one." /** * Simple getter proc for hooks to implement special hook bonuses for @@ -162,6 +172,7 @@ icon_state = "treasure" rod_overlay_icon_state = "hook_treasure_overlay" chasm_detritus_type = /datum/chasm_detritus/restricted/objects + wiki_desc = "It vastly improves the chances of catching things other than fish." /obj/item/fishing_hook/magnet/Initialize(mapload) . = ..() @@ -190,13 +201,14 @@ icon_state = "gold_shiny" fishing_hook_traits = FISHING_HOOK_SHINY rod_overlay_icon_state = "hook_shiny_overlay" + wiki_desc = "It's used to attract shiny-loving fish and make them easier to catch." /obj/item/fishing_hook/weighted name = "weighted hook" icon_state = "weighted" fishing_hook_traits = FISHING_HOOK_WEIGHTED rod_overlay_icon_state = "hook_weighted_overlay" - + wiki_desc = "It reduces the bounce that happens when you hit the boundaries of the minigame bar." /obj/item/fishing_hook/rescue name = "rescue hook" @@ -204,6 +216,8 @@ icon_state = "rescue" rod_overlay_icon_state = "hook_rescue_overlay" chasm_detritus_type = /datum/chasm_detritus/restricted/bodies + wiki_desc = "A hook used to rescue bodies whom have fallen into chasms. \ + You won't catch fish with it, nor it can't be used for fishing outside of chasms, though it can still be used to reel in people and items from unreachable locations.." /obj/item/fishing_hook/rescue/can_be_hooked(atom/target) return ..() || isliving(target) @@ -233,6 +247,7 @@ name = "bone hook" desc = "A simple hook carved from sharpened bone" icon_state = "hook_bone" + wiki_desc = "A generic fishing hook carved out of sharpened bone. Bone fishing rods come pre-equipped with it." /obj/item/fishing_hook/stabilized name = "gyro-stabilized hook" @@ -240,6 +255,8 @@ icon_state = "gyro" fishing_hook_traits = FISHING_HOOK_BIDIRECTIONAL rod_overlay_icon_state = "hook_gyro_overlay" + wiki_desc = "It allows you to move both up (left-click) and down (right-click) during the minigame while negating gravity.
\ + It requires the Advanced Fishing Technology Node to be researched to be printed." /obj/item/fishing_hook/stabilized/examine(mob/user) . = ..() @@ -252,6 +269,9 @@ w_class = WEIGHT_CLASS_NORMAL fishing_hook_traits = FISHING_HOOK_NO_ESCAPE|FISHING_HOOK_NO_ESCAPE|FISHING_HOOK_KILL rod_overlay_icon_state = "hook_jaws_overlay" + wiki_desc = "A beartrap-looking hook that makes losing the fishing minigame impossible (Unless you drop the rod or get stunned). However it'll hurt the fish and eventually kill it. \ + Funnily enough, you can snag in people with it too. It won't hurt them like a actual beartrap, but it'll still slow them down.
\ + It has to be bought from the black market uplink." /obj/item/fishing_hook/jaws/can_be_hooked(atom/target) return ..() || isliving(target) diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm index 28ae51d4236..86fe5178a6c 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/code/modules/fishing/fishing_rod.dm @@ -17,8 +17,12 @@ var/cast_range = 3 /// Fishing minigame difficulty modifier (additive) var/difficulty_modifier = 0 - /// Explaination of rod functionality shown in the ui + /// Explaination of rod functionality shown in the ui and the autowiki var/ui_description = "A classic fishing rod, with no special qualities." + /// More explaination shown in the wiki after ui_description + var/wiki_description = "" + /// Is this fishing rod shown in the wiki + var/show_in_wiki = TRUE var/obj/item/bait var/obj/item/fishing_line/line = /obj/item/fishing_line @@ -295,10 +299,11 @@ /obj/item/fishing_rod/proc/get_fishing_overlays() . = list() var/line_color = line?.line_color || default_line_color - /// Line part by the rod, always visible - var/mutable_appearance/reel_appearance = mutable_appearance(icon, reel_overlay) - reel_appearance.color = line_color - . += reel_appearance + /// Line part by the rod. + if(reel_overlay) + var/mutable_appearance/reel_appearance = mutable_appearance(icon, reel_overlay) + reel_appearance.color = line_color + . += reel_appearance // Line & hook is also visible when only bait is equipped but it uses default appearances then if(hook || bait) @@ -480,10 +485,12 @@ /obj/item/fishing_rod/unslotted hook = null line = null + show_in_wiki = FALSE /obj/item/fishing_rod/bone name = "bone fishing rod" desc = "A humble rod, made with whatever happened to be on hand." + ui_description = "A fishing rod crafted with leather, sinew and bones." icon_state = "fishing_rod_bone" reel_overlay = "reel_bone" default_line_color = "red" @@ -498,6 +505,7 @@ force = 0 w_class = WEIGHT_CLASS_NORMAL ui_description = "A collapsible fishing rod that can fit within a backpack." + wiki_description = "It has to be bought from Cargo." reel_overlay = "reel_telescopic" ///The force of the item when extended. var/active_force = 8 @@ -547,15 +555,20 @@ if(user) balloon_alert(user, active ? "extended" : "collapsed") playsound(src, 'sound/weapons/batonextend.ogg', 50, TRUE) - update_appearance(UPDATE_OVERLAYS) + update_appearance() QDEL_NULL(fishing_line) return COMPONENT_NO_DEFAULT_MESSAGE +/obj/item/fishing_rod/telescopic/update_icon_state() + . = ..() + icon_state = "[initial(icon_state)][!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE) ? "_collapsed" : ""]" + /obj/item/fishing_rod/telescopic/master name = "master fishing rod" desc = "The mythical rod of a lost fisher king. Said to be imbued with un-paralleled fishing power. There's writing on the back of the pole. \"中国航天制造\"" difficulty_modifier = -10 - ui_description = "This rod makes fishing easy even for an absolute beginner." + ui_description = "A mythical telescopic fishing rod that makes fishing quite easier." + wiki_description = null icon_state = "fishing_rod_master" reel_overlay = "reel_master" active_force = 13 //It's that sturdy @@ -566,7 +579,8 @@ /obj/item/fishing_rod/tech name = "advanced fishing rod" desc = "An embedded universal constructor along with micro-fusion generator makes this marvel of technology never run out of bait. Interstellar treaties prevent using it outside of recreational fishing. And you can fish with this. " - ui_description = "This rod has an infinite supply of synth-bait. Also doubles as an Experi-Scanner for fish." + ui_description = "A rod with an infinite supply of synthetic bait. Doubles as an Experi-Scanner for fish." + wiki_description = "It requires the Advanced Fishing Technology Node to be researched to be printed." icon_state = "fishing_rod_science" reel_overlay = "reel_science" bait = /obj/item/food/bait/doughball/synthetic/unconsumable diff --git a/code/modules/fishing/sources/_fish_source.dm b/code/modules/fishing/sources/_fish_source.dm index a63c087fcc6..2c1e1ca53e4 100644 --- a/code/modules/fishing/sources/_fish_source.dm +++ b/code/modules/fishing/sources/_fish_source.dm @@ -88,6 +88,20 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) /obj/structure/closet/crate/necropolis/tendril, )) + + ///List of multipliers used to make fishes more common compared to everything else depending on bait quality, indexed from best to worst. + var/static/weight_result_multiplier = list( + TRAIT_GREAT_QUALITY_BAIT = 9, + TRAIT_GOOD_QUALITY_BAIT = 3.5, + TRAIT_BASIC_QUALITY_BAIT = 2, + ) + ///List of exponents used to level out the table weight differences between fish depending on bait quality. + var/static/weight_leveling_exponents = list( + TRAIT_GREAT_QUALITY_BAIT = 0.7, + TRAIT_GOOD_QUALITY_BAIT = 0.55, + TRAIT_BASIC_QUALITY_BAIT = 0.4, + ) + /datum/fish_source/New() if(!PERFORM_ALL_TESTS(focus_only/fish_sources_tables)) return @@ -271,7 +285,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) /// Builds a fish weights table modified by bait/rod/user properties /datum/fish_source/proc/get_modified_fish_table(obj/item/fishing_rod/rod, mob/fisherman, atom/location) var/obj/item/bait = rod.bait - ///An exponent used to level out the difference in probabilities between fishes/mobs on the table depending on bait quality. + ///An exponent used to level out the table weight differences between fish depending on bait quality. var/leveling_exponent = 0 ///Multiplier used to make fishes more common compared to everything else. var/result_multiplier = 1 @@ -280,15 +294,12 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) var/list/final_table = fish_table.Copy() if(bait) - if(HAS_TRAIT(bait, TRAIT_GREAT_QUALITY_BAIT)) - result_multiplier = 9 - leveling_exponent = 0.5 - else if(HAS_TRAIT(bait, TRAIT_GOOD_QUALITY_BAIT)) - result_multiplier = 3.5 - leveling_exponent = 0.25 - else if(HAS_TRAIT(bait, TRAIT_BASIC_QUALITY_BAIT)) - result_multiplier = 2 - leveling_exponent = 0.1 + for(var/trait in weight_result_multiplier) + if(HAS_TRAIT(bait, trait)) + result_multiplier = weight_result_multiplier[trait] + weight_leveling_exponents = weight_leveling_exponents[trait] + break + if(HAS_TRAIT(rod, TRAIT_ROD_REMOVE_FISHING_DUD)) final_table -= FISHING_DUD @@ -308,7 +319,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) if(mult > 1 && HAS_TRAIT(bait, TRAIT_BAIT_ALLOW_FISHING_DUD)) final_table -= FISHING_DUD else - final_table[result] = round(final_table[result] * 0.15, 1) //Fishing without bait is not going to be easy + final_table[result] = round(final_table[result] * FISH_WEIGHT_MULT_WITHOUT_BAIT, 1) //Fishing without bait is not going to be easy // Apply fish trait modifiers final_table[result] = get_fish_trait_catch_mods(final_table[result], result, rod, fisherman, location) @@ -316,25 +327,29 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) if(final_table[result] <= 0) final_table -= result - ///here we even out the chances of fishie based on bait quality: better baits lead rarer fishes being more common. - if(leveling_exponent) - var/highest_fish_weight - var/list/collected_fish_weights = list() - for(var/fishable in final_table) - if(ispath(fishable, /obj/item/fish)) - var/fish_weight = fish_table[fishable] - collected_fish_weights[fishable] = fish_weight - if(fish_weight > highest_fish_weight) - highest_fish_weight = fish_weight - for(var/fish in collected_fish_weights) - var/difference = highest_fish_weight - collected_fish_weights[fish] - if(!difference) - continue - final_table[fish] += round(difference**leveling_exponent, 1) + if(leveling_exponent) + level_out_fish(final_table, leveling_exponent) return final_table +///A proc that levels out the weights of various fish, leading to rarer fishes being more common. +/datum/fish_source/proc/level_out_fish(list/table, exponent) + var/highest_fish_weight + var/list/collected_fish_weights = list() + for(var/fishable in table) + if(ispath(fishable, /obj/item/fish)) + var/fish_weight = table[fishable] + collected_fish_weights[fishable] = fish_weight + if(fish_weight > highest_fish_weight) + highest_fish_weight = fish_weight + + for(var/fish in collected_fish_weights) + var/difference = highest_fish_weight - collected_fish_weights[fish] + if(!difference) + continue + table[fish] += round(difference**exponent, 1) + /datum/fish_source/proc/get_fish_trait_catch_mods(weight, obj/item/fish/fish, obj/item/fishing_rod/rod, mob/user, atom/location) if(!ispath(fish, /obj/item/fish)) return weight @@ -425,3 +440,101 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons()) reward.pixel_y = rand(-9, 9) if(severity >= EXPLODE_DEVASTATE) reward.ex_act(EXPLODE_LIGHT) + +/** + * Called by /datum/autowiki/fish_sources unless the catalog entry for this fish source is null. + * It should Return a list of entries with keys named "name", "icon", "weight" and "notes" + * detailing the contents of this fish source. + */ +/datum/fish_source/proc/generate_wiki_contents(datum/autowiki/fish_sources/wiki) + var/list/data = list() + var/list/only_fish = list() + + var/total_weight = 0 + var/total_weight_without_bait = 0 + var/total_weight_no_fish = 0 + + var/list/tables_by_quality = list() + var/list/total_weight_by_quality = list() + var/list/total_weight_by_quality_no_fish = list() + + for(var/obj/item/fish/fish as anything in fish_table) + var/weight = fish_table[fish] + if(fish != FISHING_DUD) + total_weight += weight + if(!ispath(fish, /obj/item/fish)) + total_weight_without_bait += weight + total_weight_no_fish += weight + continue + if(initial(fish.show_in_catalog)) + only_fish += fish + total_weight_without_bait += round(fish_table[fish] * FISH_WEIGHT_MULT_WITHOUT_BAIT, 1) + + for(var/trait in weight_result_multiplier) + var/list/table_copy = fish_table.Copy() + table_copy -= FISHING_DUD + var/exponent = weight_leveling_exponents[trait] + var/multiplier = weight_result_multiplier[trait] + for(var/fish as anything in table_copy) + if(!ispath(fish, /obj/item/fish)) + continue + table_copy[fish] = round(table_copy[fish] * multiplier, 1) + + level_out_fish(table_copy, exponent) + tables_by_quality[trait] = table_copy + + var/tot_weight = 0 + var/tot_weight_no_fish = 0 + for(var/result in table_copy) + var/weight = table_copy[result] + tot_weight += weight + if(!ispath(result, /obj/item/fish)) + tot_weight_no_fish += weight + total_weight_by_quality[trait] = tot_weight + total_weight_by_quality_no_fish[trait] = tot_weight_no_fish + + //show the improved weights in ascending orders for fish. + tables_by_quality = reverseList(tables_by_quality) + + if(FISHING_DUD in fish_table) + data += LIST_VALUE_WRAP_LISTS(list( + FISH_SOURCE_AUTOWIKI_NAME = FISH_SOURCE_AUTOWIKI_DUD, + FISH_SOURCE_AUTOWIKI_ICON = "", + FISH_SOURCE_AUTOWIKI_WEIGHT = PERCENT(fish_table[FISHING_DUD]/total_weight_without_bait), + FISH_SOURCE_AUTOWIKI_WEIGHT_SUFFIX = "WITHOUT A BAIT", + FISH_SOURCE_AUTOWIKI_NOTES = "Unless you have a magnet or rescue hook or you know what you're doing, always use a bait", + )) + + for(var/obj/item/fish/fish as anything in only_fish) + var/weight = fish_table[fish] + var/deets = "Can be caught indefinitely" + if(fish in fish_counts) + deets = "It's quite rare and can only be caught up to [fish_counts[fish]] times" + if(fish in fish_count_regen) + deets += " every [DisplayTimeText(fish::breeding_timeout)]" + var/list/weight_deets = list() + for(var/trait in tables_by_quality) + weight_deets += "[round(PERCENT(tables_by_quality[trait][fish]/total_weight_by_quality[trait]), 0.1)]%" + var/weight_suffix = "([english_list(weight_deets, and_text = ", ")])" + data += LIST_VALUE_WRAP_LISTS(list( + FISH_SOURCE_AUTOWIKI_NAME = wiki.escape_value(full_capitalize(initial(fish.name))), + FISH_SOURCE_AUTOWIKI_ICON = FISH_AUTOWIKI_FILENAME(fish), + FISH_SOURCE_AUTOWIKI_WEIGHT = PERCENT(weight/total_weight), + FISH_SOURCE_AUTOWIKI_WEIGHT_SUFFIX = weight_suffix, + FISH_SOURCE_AUTOWIKI_NOTES = deets, + )) + + if(total_weight_no_fish) //There are things beside fish that we can catch. + var/list/weight_deets = list() + for(var/trait in tables_by_quality) + weight_deets += "[round(PERCENT(total_weight_by_quality_no_fish[trait]/total_weight_by_quality[trait]), 0.1)]%" + var/weight_suffix = "([english_list(weight_deets, and_text = ", ")])" + data += LIST_VALUE_WRAP_LISTS(list( + FISH_SOURCE_AUTOWIKI_NAME = FISH_SOURCE_AUTOWIKI_OTHER, + FISH_SOURCE_AUTOWIKI_ICON = FISH_SOURCE_AUTOWIKI_QUESTIONMARK, + FISH_SOURCE_AUTOWIKI_WEIGHT = PERCENT(total_weight_no_fish/total_weight), + FISH_SOURCE_AUTOWIKI_WEIGHT_SUFFIX = weight_suffix, + FISH_SOURCE_AUTOWIKI_NOTES = "Who knows what it may be. Try and find out", + )) + + return data diff --git a/code/modules/fishing/sources/source_types.dm b/code/modules/fishing/sources/source_types.dm index 6aea78b9943..ffbb2bb5332 100644 --- a/code/modules/fishing/sources/source_types.dm +++ b/code/modules/fishing/sources/source_types.dm @@ -370,7 +370,7 @@ fish_table = list( FISHING_DUD = 20, /obj/item/fish/ratfish = 10, - /obj/item/fish/slimefish = 4 + /obj/item/fish/slimefish = 4, ) fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 10 @@ -401,6 +401,15 @@ ) fishing_difficulty = FISHING_EASY_DIFFICULTY +/datum/fish_source/holographic/generate_wiki_contents(datum/autowiki/fish_sources/wiki) + var/obj/item/fish/prototype = /obj/item/fish/holo/checkered + return LIST_VALUE_WRAP_LISTS(list( + FISH_SOURCE_AUTOWIKI_NAME = "Holographic Fish", + FISH_SOURCE_AUTOWIKI_ICON = FISH_AUTOWIKI_FILENAME(prototype), + FISH_SOURCE_AUTOWIKI_WEIGHT = 100, + FISH_SOURCE_AUTOWIKI_NOTES = "Holographic fish disappears outside the Holodeck", + )) + /datum/fish_source/holographic/reason_we_cant_fish(obj/item/fishing_rod/rod, mob/fisherman, atom/parent) . = ..() if(!istype(get_area(fisherman), /area/station/holodeck)) @@ -445,7 +454,7 @@ fish_table = list( FISHING_DUD = 25, /obj/item/food/grown/grass = 25, - RANDOM_SEED = 16, + FISHING_RANDOM_SEED = 16, /obj/item/seeds/grass = 6, /obj/item/seeds/random = 1, /mob/living/basic/frog = 1, @@ -454,13 +463,55 @@ fish_counts = list( /obj/item/food/grown/grass = 10, /obj/item/seeds/grass = 4, - RANDOM_SEED = 4, + FISHING_RANDOM_SEED = 4, /obj/item/seeds/random = 1, /mob/living/basic/frog = 1, /mob/living/basic/axolotl = 1, ) fishing_difficulty = FISHING_EASY_DIFFICULTY - 5 +/datum/fish_source/hydro_tray/generate_wiki_contents(datum/autowiki/fish_sources/wiki) + var/list/data = list() + var/total_weight = 0 + var/critter_weight = 0 + var/seed_weight = 0 + var/other_weight = 0 + var/dud_weight = fish_table[FISHING_DUD] + for(var/content in fish_table) + var/weight = fish_table[content] + total_weight += weight + if(ispath(content, /mob/living)) + critter_weight += weight + else if(ispath(content, /obj/item/food/grown) || ispath(content, /obj/item/seeds) || content == FISHING_RANDOM_SEED) + seed_weight += weight + else if(content != FISHING_DUD) + other_weight += weight + + data += LIST_VALUE_WRAP_LISTS(list( + FISH_SOURCE_AUTOWIKI_NAME = FISH_SOURCE_AUTOWIKI_DUD, + FISH_SOURCE_AUTOWIKI_DUD = "", + FISH_SOURCE_AUTOWIKI_WEIGHT = PERCENT(dud_weight/total_weight), + FISH_SOURCE_AUTOWIKI_WEIGHT_SUFFIX = "WITHOUT A BAIT", + FISH_SOURCE_AUTOWIKI_NOTES = "", + )) + + data += LIST_VALUE_WRAP_LISTS(list( + FISH_SOURCE_AUTOWIKI_NAME = "Critter", + FISH_SOURCE_AUTOWIKI_DUD = "", + FISH_SOURCE_AUTOWIKI_WEIGHT = PERCENT(critter_weight/total_weight), + FISH_SOURCE_AUTOWIKI_NOTES = "A small creature, usually a frog or an axolotl", + )) + + if(other_weight) + data += LIST_VALUE_WRAP_LISTS(list( + FISH_SOURCE_AUTOWIKI_NAME = "Other Stuff", + FISH_SOURCE_AUTOWIKI_DUD = "", + FISH_SOURCE_AUTOWIKI_WEIGHT = PERCENT(other_weight/total_weight), + FISH_SOURCE_AUTOWIKI_NOTES = "Other stuff, who knows...", + )) + + return data + /datum/fish_source/hydro_tray/reason_we_cant_fish(obj/item/fishing_rod/rod, mob/fisherman, atom/parent) if(!istype(parent, /obj/machinery/hydroponics/constructable)) return ..() diff --git a/code/modules/research/techweb/_techweb.dm b/code/modules/research/techweb/_techweb.dm index 1a5a3875511..fd6ca7949b4 100644 --- a/code/modules/research/techweb/_techweb.dm +++ b/code/modules/research/techweb/_techweb.dm @@ -317,11 +317,7 @@ var/points_rewarded if(completed_experiment.points_reward) add_point_list(completed_experiment.points_reward) - points_rewarded = ",[refund > 0 ? " and" : ""] rewarding " - var/list/english_list_keys = list() - for(var/points_type in completed_experiment.points_reward) - english_list_keys += "[completed_experiment.points_reward[points_type]] [points_type]" - points_rewarded += "[english_list(english_list_keys)] points" + points_rewarded = ",[refund > 0 ? " and" : ""] rewarding [completed_experiment.get_points_reward_text()]" result_text += points_rewarded result_text += "!" diff --git a/code/modules/unit_tests/fish_unit_tests.dm b/code/modules/unit_tests/fish_unit_tests.dm index 2fb2487f781..e3ede57fc29 100644 --- a/code/modules/unit_tests/fish_unit_tests.dm +++ b/code/modules/unit_tests/fish_unit_tests.dm @@ -84,6 +84,7 @@ fish_traits = list(/datum/fish_trait/dummy) stable_population = INFINITY breeding_timeout = 0 + show_in_catalog = FALSE //skipped by the autowiki unit test. /obj/item/fish/testdummy/two fish_traits = list(/datum/fish_trait/dummy/two) diff --git a/icons/obj/fishing.dmi b/icons/obj/fishing.dmi index 2344d2b8ed9..9c6a535fd83 100644 Binary files a/icons/obj/fishing.dmi and b/icons/obj/fishing.dmi differ diff --git a/icons/obj/storage/box.dmi b/icons/obj/storage/box.dmi index 747fb5462c7..79f7eff2a67 100644 Binary files a/icons/obj/storage/box.dmi and b/icons/obj/storage/box.dmi differ diff --git a/tgstation.dme b/tgstation.dme index f8e32dacc97..2aaf7a3701b 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3515,6 +3515,7 @@ #include "code\modules\atmospherics\machinery\portable\scrubber.dm" #include "code\modules\autowiki\autowiki.dm" #include "code\modules\autowiki\pages\base.dm" +#include "code\modules\autowiki\pages\fishing.dm" #include "code\modules\autowiki\pages\soup.dm" #include "code\modules\autowiki\pages\stockparts.dm" #include "code\modules\autowiki\pages\techweb.dm"