diff --git a/code/__DEFINES/fantasy_affixes.dm b/code/__DEFINES/fantasy_affixes.dm index 709d414d117c..01636882a40f 100644 --- a/code/__DEFINES/fantasy_affixes.dm +++ b/code/__DEFINES/fantasy_affixes.dm @@ -2,4 +2,10 @@ #define AFFIX_SUFFIX (1 << 1) #define AFFIX_GOOD (1 << 0) -#define AFFIX_EVIL (1 << 1) \ No newline at end of file +#define AFFIX_EVIL (1 << 1) + +#define TIER_NORMAL "normal" //white +#define TIER_UNCOMMON "uncommon" //green +#define TIER_RARE "rare" //blue +#define TIER_LEGENDARY "epic" //urple +#define TIER_MYTHICAL "mythical" //orange diff --git a/code/datums/components/fantasy/_fantasy.dm b/code/datums/components/fantasy/_fantasy.dm index 3a879b4e2370..63441f9a829d 100644 --- a/code/datums/components/fantasy/_fantasy.dm +++ b/code/datums/components/fantasy/_fantasy.dm @@ -1,3 +1,27 @@ +GLOBAL_LIST_INIT(rarity_to_quality, list( + TIER_NORMAL = 0, + TIER_UNCOMMON = 2, + TIER_RARE = 5, + TIER_LEGENDARY = 10, + TIER_MYTHICAL = 25 +)) + +GLOBAL_LIST_INIT(rarity_to_color, list( + TIER_NORMAL = "#FFFFFF", + TIER_UNCOMMON = "#00ff62", + TIER_RARE = "#2600ff", + TIER_LEGENDARY = "#ff00ff", + TIER_MYTHICAL = "#ffd900" +)) + +GLOBAL_LIST_INIT(rarity_weights, list( + TIER_NORMAL = 55, + TIER_UNCOMMON = 30, + TIER_RARE = 10, + TIER_LEGENDARY = 4, + TIER_MYTHICAL = 1 + )) + /datum/component/fantasy dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS @@ -12,13 +36,17 @@ var/static/list/affixListing + var/rarity = TIER_NORMAL + /datum/component/fantasy/Initialize(quality, list/affixes = list(), canFail=FALSE, announce=FALSE) if(!isitem(parent)) return COMPONENT_INCOMPATIBLE - src.quality = quality || randomQuality() + // src.quality = quality || randomQuality() src.canFail = canFail src.announce = announce + src.rarity = randomRarity() + src.quality = GLOB.rarity_to_quality[src.rarity] src.affixes = affixes appliedComponents = list() @@ -39,21 +67,12 @@ /datum/component/fantasy/InheritComponent(datum/component/fantasy/newComp, original, quality, list/affixes, canFail, announce) unmodify() - if(newComp) - src.quality += newComp.quality - src.canFail = newComp.canFail - src.announce = newComp.announce - else - src.quality += quality - src.canFail = canFail || canFail - src.announce = announce || announce + src.rarity = randomRarity() + src.quality = GLOB.rarity_to_quality[src.rarity] modify() -/datum/component/fantasy/proc/randomQuality() - var/quality = pick(1;15, 2;14, 2;13, 2;12, 3;11, 3;10, 3;9, 4;8, 4;7, 4;6, 5;5, 5;4, 5;3, 6;2, 6;1, 6;0) - if(prob(50)) - quality = -quality - return quality +/datum/component/fantasy/proc/randomRarity() + return pickweight(GLOB.rarity_weights) /datum/component/fantasy/proc/randomAffixes(force) if(!affixListing) @@ -100,7 +119,12 @@ newName = affix.apply(src, newName) if(quality != 0) - newName = "[newName] [quality > 0 ? "+" : ""][quality]" + newName = "[newName][quality > 0 ? "+" : ""][quality]" + + var/rarity_string = rarity == TIER_NORMAL ? "" : "[rarity] " + newName = "[rarity_string][newName]" + + master.color = GLOB.rarity_to_color[rarity] if(canFail && prob((quality - 9)*10)) var/turf/place = get_turf(parent) @@ -128,6 +152,7 @@ master.bare_wound_bonus -= quality master.name = originalName + master.color = null /datum/component/fantasy/proc/announce() var/turf/location = get_turf(parent) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index a4af6863e9bf..ad41769e3ad2 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1,6 +1,6 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/effects/fire.dmi', FIRE)) -GLOBAL_VAR_INIT(rpg_loot_items, FALSE) +GLOBAL_VAR_INIT(rpg_loot_items, TRUE) // if true, everyone item when created will have its name changed to be // more... RPG-like. diff --git a/code/modules/af/anvil.dm b/code/modules/af/anvil.dm new file mode 100644 index 000000000000..4733eabe58b4 --- /dev/null +++ b/code/modules/af/anvil.dm @@ -0,0 +1,67 @@ +#define SUPER_CREDIT_REROLL_COST 1 +GLOBAL_VAR_INIT(anvil_cooldown, 1 SECONDS) + +/obj/machinery/anvil + name = "Enchantment Anvil" + desc = "Used to enchant your weapons and armor to Win The Game!" + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + icon = 'icons/obj/lavaland/anvil.dmi' + icon_state = "fool" + density = TRUE + var/list/concurrent_users = list() + +/obj/machinery/anvil/Destroy() + QDEL_LIST_ASSOC_VAL(concurrent_users) + return ..() + +/obj/machinery/anvil/attack_hand(mob/living/user) + . = ..() + if(.) + return + use_anvil_ui(user) + +/obj/machinery/anvil/attackby(obj/item/reforge_target, mob/user, params) + if(!reforge_target.GetComponent(/datum/component/fantasy)) + to_chat(user, span_danger("You can't enchant this item.")) + return + + use_anvil_ui(user, reforge_target) + +/obj/machinery/anvil/proc/use_anvil_ui(mob/user, obj/item/reforge_target) + + if(!concurrent_users[user.ckey]) + concurrent_users[user.ckey] = new /datum/reforge_menu(user, src) + + var/datum/reforge_menu/player_menu = concurrent_users[user.ckey] + if(reforge_target) + player_menu.set_target_item(reforge_target) + + player_menu.ui_interact(user) + +/obj/machinery/anvil/proc/reroll(obj/item/reforge_target, mob/user, datum/reforge_menu/player_menu) + if(player_menu.use_super_credits) + if(user.mind.super_credits < SUPER_CREDIT_REROLL_COST) + to_chat(user, "You don't have any Super Credit(TM) to use!") + player_menu.use_super_credits = FALSE + return + to_chat(user, "You spend a Super Credit(TM) to boost your reroll chances!") + user.mind.super_credits -= SUPER_CREDIT_REROLL_COST + var/old_name = reforge_target.name + var/datum/component/fantasy/F = reforge_target.GetComponent(/datum/component/fantasy) + reforge_target.AddComponent(/datum/component/fantasy, 0, null, null, FALSE, FALSE) //this is bad code + var/new_rarity = F.rarity + playsound(get_turf(src), "sound/effects/anvil/tinker.ogg", 40) + to_chat(user, "You reroll your [old_name] to a [reforge_target.name]!") + set_light(l_range = 3, l_power = 3, l_color = GLOB.rarity_to_color[new_rarity]) + addtimer(CALLBACK(src, PROC_REF(reset_light)), GLOB.anvil_cooldown, TIMER_UNIQUE|TIMER_OVERRIDE) + if(new_rarity == TIER_LEGENDARY || new_rarity == TIER_MYTHICAL) + for(var/mob/M in SSmobs.clients_by_zlevel[z]) + to_chat(M, span_alert("[user] has rolled [reforge_target]!")) + if(M.client.prefs.toggles & SOUND_AMBIENCE) + playsound(get_turf(src), "sound/effects/anvil/congrats.ogg", 60, extrarange = 100) //EVERYONE + playsound(get_turf(src), "sound/effects/anvil/fireworks.ogg", 60, extrarange = 100) + COOLDOWN_START(player_menu, reroll_cooldown, GLOB.anvil_cooldown) + + +/obj/machinery/anvil/proc/reset_light() + set_light(l_range = 0, l_power = 0, l_color = null) diff --git a/code/modules/af/mind.dm b/code/modules/af/mind.dm new file mode 100644 index 000000000000..45875972c98c --- /dev/null +++ b/code/modules/af/mind.dm @@ -0,0 +1,14 @@ +/datum/mind + var/super_credits = -1 + +/datum/mind/proc/initialize_credits() + if(super_credits == -1) + super_credits = 10 + if(is_donator(current)) + super_credits += 10 + if(is_donator(current) && (!is_admin(current) && !is_deadmin(current) && !is_mentor(current))) //actual donators get 20 more + super_credits += 20 + +/mob/sync_mind() + . = ..() + mind?.initialize_credits() diff --git a/code/modules/af/reforge_menu.dm b/code/modules/af/reforge_menu.dm new file mode 100644 index 000000000000..93a74a54284f --- /dev/null +++ b/code/modules/af/reforge_menu.dm @@ -0,0 +1,86 @@ +/datum/reforge_menu + var/mob/owner + var/obj/machinery/anvil/parent_anvil + var/obj/item/reforge_target + var/use_super_credits = FALSE + COOLDOWN_DECLARE(reroll_cooldown) + +/datum/reforge_menu/New(mob/new_owner, obj/machinery/anvil/parent_anvil) + if(!istype(new_owner)) + qdel(src) + src.owner = new_owner + src.parent_anvil = parent_anvil + + RegisterSignal(parent_anvil, COMSIG_QDELETING, PROC_REF(on_anvil_destroyed)) + +/datum/reforge_menu/ui_state(mob/user) + return GLOB.physical_state + +/datum/reforge_menu/Destroy() + owner = null + parent_anvil = null + reforge_target = null + return ..() + +/datum/reforge_menu/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if (!ui) + ui = new(user, src, "ReforgerMenu") + ui.open() + +/datum/reforge_menu/ui_host() + return parent_anvil + +/datum/reforge_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + switch(action) + if ("reroll") + if(!COOLDOWN_FINISHED(src, reroll_cooldown)) + to_chat(owner, span_danger("The cooldown for rerolling hasn't finished yet.")) + return + parent_anvil.reroll(reforge_target, owner, src) + . = TRUE + if ("toggle_super_credits") + use_super_credits = !use_super_credits + . = TRUE + +/datum/reforge_menu/ui_data(mob/user) + var/list/data = list() + data["reforge_cooldown"] = reroll_cooldown - world.time + data["super_credits_available"] = user.mind.super_credits + data["use_super_credits"] = use_super_credits + data["reforge_target"] = null + if(reforge_target) + var/list/details = list() + details["name"] = reforge_target.name + details["description"] = reforge_target.desc + var/datum/component/fantasy/fantasy_affix = reforge_target.GetComponent(/datum/component/fantasy) + details["rarity"] = list("name" = fantasy_affix.rarity, "color" = GLOB.rarity_to_color[fantasy_affix.rarity], "chances" = GLOB.rarity_weights[fantasy_affix.rarity]) + details["item_pic"] = icon2base64(getFlatIcon(reforge_target)) + data["reforge_target"] = details + return data + +/datum/reforge_menu/ui_static_data(mob/user) + var/list/data = list() + data["rarities"] = list() + var/rarity_total_weight = 0 + for(var/rarity in GLOB.rarity_weights) + var/list/rarity_data = list() + rarity_data["name"] = rarity + rarity_data["color"] = GLOB.rarity_to_color[rarity] + rarity_data["weight"] = GLOB.rarity_weights[rarity] + + data["rarities"] += list(rarity_data) + rarity_total_weight += GLOB.rarity_weights[rarity] + + data["rarity_total_weight"] = rarity_total_weight + return data + +/datum/reforge_menu/proc/set_target_item(obj/item/new_reforge_target) + src.reforge_target = new_reforge_target + +/datum/reforge_menu/proc/on_anvil_destroyed() + qdel(src) + diff --git a/icons/obj/lavaland/anvil.dmi b/icons/obj/lavaland/anvil.dmi index 97e164b8e222..c603ba5ff887 100644 Binary files a/icons/obj/lavaland/anvil.dmi and b/icons/obj/lavaland/anvil.dmi differ diff --git a/sound/effects/anvil/congrats.ogg b/sound/effects/anvil/congrats.ogg new file mode 100644 index 000000000000..2eef5ca1d0eb Binary files /dev/null and b/sound/effects/anvil/congrats.ogg differ diff --git a/sound/effects/anvil/fireworks.ogg b/sound/effects/anvil/fireworks.ogg new file mode 100644 index 000000000000..a01cd601fd89 Binary files /dev/null and b/sound/effects/anvil/fireworks.ogg differ diff --git a/sound/effects/anvil/tinker.ogg b/sound/effects/anvil/tinker.ogg new file mode 100644 index 000000000000..c7747cb41c04 Binary files /dev/null and b/sound/effects/anvil/tinker.ogg differ diff --git a/tgui/packages/tgui/interfaces/ReforgerMenu.tsx b/tgui/packages/tgui/interfaces/ReforgerMenu.tsx new file mode 100644 index 000000000000..651333ca099e --- /dev/null +++ b/tgui/packages/tgui/interfaces/ReforgerMenu.tsx @@ -0,0 +1,179 @@ +import { useBackend, useLocalState } from '../backend'; +import { + Button, + LabeledList, + Section, + Tabs, + Stack, + Box, + TextArea, +} from '../components'; +import { Window } from '../layouts'; +import { resolveAsset } from '../assets'; +import { capitalize } from 'common/string'; + +type Data = { + reforge_target: Item | null; + fantasyComp: FantasyComp; + rarities: Rarity[]; + rarity_total_weight: number; + use_super_credits: boolean; + super_credits_available: number; +}; + +type FantasyComp = { + name: string; + rarity: string; +}; + +type Item = { + name: string; + description: string; + rarity: Rarity; + item_pic: string; +}; + +type Rarity = { + name: string; + weight: number; + color: string; +}; + +export const ReforgerMenu = (props, context) => { + const { act, data } = useBackend(context); + const { reforge_target, rarities, use_super_credits } = data; + const modified_rarities = [30, 35, 20, 10, 5]; + + return ( + + + + + + + + + + + +
+ + {rarities.map((rarity) => ( + + {capitalize(rarity.name)}:{' '} + {use_super_credits + ? modified_rarities[ + rarities.findIndex( + (raritykey) => raritykey === rarity + ) + ] + : rarity.weight} + % + + ))} + +
+
+
+
+
+
+
+ ); +}; + +const ItemPreview = (props, context) => { + const { act, data } = useBackend(context); + const { + reforge_target = null, + rarities, + use_super_credits, + super_credits_available, + } = data; + const item_rarity = reforge_target?.rarity || rarities[0]; + const item_color = item_rarity?.color || 'white'; + const font_size = Math.max( + 24, + 16 * (rarities.findIndex((rarity) => rarity.name === item_rarity.name) + 1) + ); + const [readTerms, setReadTermsChecked] = useLocalState( + context, + 'readTerms', + false + ); + + if (reforge_target !== null) { + return ( +
+ + + {capitalize(reforge_target.name)} + + {reforge_target.description} + + + + + + + act('toggle_super_credits')} + /> + + {super_credits_available} Super Credits(TM) available to you. + + { + setReadTermsChecked(!readTerms); + }} + /> + + +
+ ); + } else { + return ( +
+ + + Place an item on the anvil to get started and let fate take the + wheel. + + +
+ ); + } +};