diff --git a/_maps/virtual_domains/stairs_and_cliffs.dmm b/_maps/virtual_domains/stairs_and_cliffs.dmm index 3c9e0711e02..accdf93bf00 100644 --- a/_maps/virtual_domains/stairs_and_cliffs.dmm +++ b/_maps/virtual_domains/stairs_and_cliffs.dmm @@ -5,6 +5,10 @@ }, /turf/open/cliff/snowrock/virtual_domain, /area/icemoon/underground/explored/virtual_domain) +"bU" = ( +/mob/living/basic/bear/snow/ancient, +/turf/open/misc/asteroid/snow, +/area/icemoon/underground/explored/virtual_domain) "cu" = ( /obj/effect/turf_decal/weather/snow/corner{ dir = 4 @@ -19,6 +23,10 @@ /obj/structure/chair/sofa/bench, /turf/open/floor/plating/snowed/smoothed, /area/icemoon/underground/explored/virtual_domain) +"cS" = ( +/obj/structure/barricade/wooden/snowed, +/turf/open/floor/iron/stairs, +/area/icemoon/underground/explored/virtual_domain) "dR" = ( /turf/open/misc/asteroid/snow, /area/icemoon/underground/explored/virtual_domain) @@ -34,6 +42,14 @@ /obj/structure/railing/corner, /turf/open/cliff/snowrock/virtual_domain, /area/icemoon/underground/explored/virtual_domain) +"gF" = ( +/obj/effect/landmark/bitrunning/curiosity_spawn, +/turf/open/misc/asteroid/snow, +/area/icemoon/underground/explored/virtual_domain) +"gY" = ( +/obj/effect/decal/remains/human, +/turf/open/misc/asteroid/snow, +/area/icemoon/underground/explored/virtual_domain) "hc" = ( /obj/structure/railing/corner/end{ dir = 8 @@ -65,6 +81,10 @@ /obj/structure/flora/tree/pine/style_random, /turf/open/misc/asteroid/snow, /area/icemoon/underground/explored/virtual_domain) +"mr" = ( +/obj/structure/fluff/fokoff_sign, +/turf/open/misc/asteroid/snow, +/area/icemoon/underground/explored/virtual_domain) "mx" = ( /obj/structure/railing, /obj/structure/railing{ @@ -86,6 +106,13 @@ }, /turf/open/floor/wood, /area/icemoon/underground/explored/virtual_domain) +"nY" = ( +/obj/structure/railing{ + dir = 1 + }, +/obj/structure/railing, +/turf/open/floor/wood, +/area/icemoon/underground/explored/virtual_domain) "pl" = ( /obj/structure/bonfire/prelit, /turf/open/misc/asteroid/snow, @@ -597,10 +624,10 @@ dR dR dR dR -sM -sM -sM -sM +Nv +Nv +Nv +dR sM sM sM @@ -672,23 +699,23 @@ Qv Qv kK sw -dR +gY dR sM sM sM +dR +cS +Nv +Nv +dR +dR +sw sM sM sM -sM -sM -sM -sM -sM -sM -sM -sM -sM +qc +gF sM sM sM @@ -754,18 +781,18 @@ dR sM sM sM +sa sM sM +gY +dR +dR +sa +dR sM sM -sM -sM -sM -sM -sM -sM -sM -sM +Dz +dR sM sM sM @@ -827,21 +854,21 @@ Qv eB sw dR +mr +sM +sM +sM +sM +sM +sM +mr +sa dR +dR +gY sM sM -sM -sM -sM -sM -sM -sM -sM -sM -sM -sM -sM -sM +nY sM sM sM @@ -901,24 +928,24 @@ Am Am Qv Qv -dR +mr eB dR -sw +dR dR sM sM sM sM sM -sM -sM -sM -sM -sM -sM -sM -sM +kK +dR +bU +dR +dR +cS +Nv +hc sM sM sM @@ -989,10 +1016,10 @@ sM sM sM sM -sM -sM -sM -sM +dR +sa +gY +dR sM sM sM @@ -1063,13 +1090,13 @@ dR sM sM sM +pL sM sM -sM -sM -sM -sM -sM +sw +dR +dR +sa sM sM sM @@ -1140,13 +1167,13 @@ sM sM sM sM +pL +pL sM sM -sM -sM -sM -sM -sM +dR +dR +sw sM sM sM @@ -1217,8 +1244,8 @@ sM sM sM sM -sM -sM +pL +pL sM sM sM @@ -1295,11 +1322,11 @@ sM sM sM sM -sM -sM -sM -sM -sM +pL +pL +pL +pL +pL sM sM sM @@ -1373,8 +1400,8 @@ sM sM sM sM -sM -sM +pL +pL sM sM sM diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm index 08c06ebcc73..381fe5eef79 100644 --- a/code/__DEFINES/access.dm +++ b/code/__DEFINES/access.dm @@ -193,6 +193,11 @@ /// HUNTERS #define ACCESS_HUNTER "hunter" +/// - - - MISC - - - + // These don't really fit anywhere else +/// For things that aren't ever supposed to be accessed +#define ACCESS_INACCESSIBLE "inaccessible" + /// - - - END ACCESS IDS - - - /// A list of access levels that, when added to an ID card, will warn admins. diff --git a/code/controllers/subsystem/bitrunning.dm b/code/controllers/subsystem/bitrunning.dm new file mode 100644 index 00000000000..2b303911e42 --- /dev/null +++ b/code/controllers/subsystem/bitrunning.dm @@ -0,0 +1,60 @@ +#define REDACTED "???" + +SUBSYSTEM_DEF(bitrunning) + name = "Bitrunning" + flags = SS_NO_FIRE + + var/list/all_domains = list() + +/datum/controller/subsystem/bitrunning/Initialize() + InitializeDomains() + return SS_INIT_SUCCESS + +/datum/controller/subsystem/bitrunning/proc/InitializeDomains() + for(var/path in subtypesof(/datum/lazy_template/virtual_domain)) + all_domains += new path() + +/// Compiles a list of available domains. +/datum/controller/subsystem/bitrunning/proc/get_available_domains(scanner_tier, points) + var/list/levels = list() + + for(var/datum/lazy_template/virtual_domain/domain as anything in all_domains) + if(domain.test_only) + continue + var/can_view = domain.difficulty < scanner_tier && domain.cost <= points + 5 + var/can_view_reward = domain.difficulty < (scanner_tier + 1) && domain.cost <= points + 3 + + levels += list(list( + "cost" = domain.cost, + "desc" = can_view ? domain.desc : "Limited scanning capabilities. Cannot infer domain details.", + "difficulty" = domain.difficulty, + "id" = domain.key, + "is_modular" = domain.is_modular, + "has_secondary_objectives" = assoc_value_sum(domain.secondary_loot) ? TRUE : FALSE, + "name" = can_view ? domain.name : REDACTED, + "reward" = can_view_reward ? domain.reward_points : REDACTED, + )) + + return levels + +/datum/controller/subsystem/bitrunning/proc/pick_secondary_loot(completed_domain) + var/datum/lazy_template/virtual_domain/domain = completed_domain + var/choice + + if(assoc_value_sum(domain.secondary_loot)) + choice = pick_weight(domain.secondary_loot) + domain.secondary_loot[choice] -= 1 + else + choice = /obj/item/paper/paperslip/bitrunning_error + CRASH("Virtual domain [domain.name] tried to pick secondary objective loot, but secondary_loot list was empty.") + return choice + +/obj/item/paper/paperslip/bitrunning_error + name = "Apology Letter" + desc = "Something went wrong here." + +/obj/item/paper/paperslip/bitrunning_error/Initialize(mapload) + default_raw_text = "Your reward for collecting the encrypted curiosity failed to arrive, please report this to technical support." + return ..() + +#undef REDACTED diff --git a/code/modules/bitrunning/objects/byteforge.dm b/code/modules/bitrunning/objects/byteforge.dm index 5f089bf66ca..f8212b7666b 100644 --- a/code/modules/bitrunning/objects/byteforge.dm +++ b/code/modules/bitrunning/objects/byteforge.dm @@ -63,5 +63,5 @@ /obj/machinery/byteforge/proc/start_to_spawn(obj/cache) flicker() - addtimer(CALLBACK(src, PROC_REF(spawn_cache), cache), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE) + addtimer(CALLBACK(src, PROC_REF(spawn_cache), cache), 1 SECONDS) diff --git a/code/modules/bitrunning/objects/landmarks.dm b/code/modules/bitrunning/objects/landmarks.dm index 3a90939dae1..3b38493edff 100644 --- a/code/modules/bitrunning/objects/landmarks.dm +++ b/code/modules/bitrunning/objects/landmarks.dm @@ -22,6 +22,11 @@ name = "Bitrunning crate spawn" icon_state = "crate" +/// Where you want secondary objectives to spawn +/obj/effect/landmark/bitrunning/curiosity_spawn + name = "Bitrunning curiosity spawn" + icon_state = "crate" + ///Swaps the locations of an encrypted crate in the area with another randomly selected crate. ///Randomizes names, so you have to inspect crates manually. /obj/effect/landmark/bitrunning/crate_replacer diff --git a/code/modules/bitrunning/objects/loot_box.dm b/code/modules/bitrunning/objects/loot_box.dm new file mode 100644 index 00000000000..39209c33d97 --- /dev/null +++ b/code/modules/bitrunning/objects/loot_box.dm @@ -0,0 +1,53 @@ +/obj/item/storage/lockbox/bitrunning + name = "base class curiosity" + desc = "Talk to a coder." + req_access = list(ACCESS_INACCESSIBLE) + icon_state = "bitrunning+l" + inhand_icon_state = "bitrunning" + base_icon_state = "bitrunning" + icon_locked = "bitrunning+l" + icon_closed = "bitrunning" + icon_broken = "bitrunning+b" + +/obj/item/storage/lockbox/bitrunning/encrypted + name = "encrypted curiosity" + desc = "Needs to be decrypted at the safehouse to be opened." + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + /// Path for the loot we are assigned + var/loot_path + +/obj/item/storage/lockbox/bitrunning/encrypted/emag_act(mob/user, obj/item/card/emag/emag_card) + return FALSE + +/obj/item/storage/lockbox/bitrunning/decrypted + name = "decrypted curiosity" + desc = "Compiled from the virtual domain. An extra reward of a successful bitrunner." + /// What virtual domain did we come from. + var/datum/lazy_template/virtual_domain/source_domain + +/obj/item/storage/lockbox/bitrunning/decrypted/Initialize( + mapload, + datum/lazy_template/virtual_domain/completed_domain, + ) + + if(isnull(completed_domain)) + log_runtime("Decrypted curiosity was created with no source domain.") + return INITIALIZE_HINT_QDEL + + if(!istype(completed_domain, /datum/lazy_template/virtual_domain)) // Check if this is a proper virtual domain before doing anything with it + log_runtime("Decrypted curiosity was created with an invalid source domain. [completed_domain.name] ([completed_domain.type]).") + return INITIALIZE_HINT_QDEL + + source_domain = completed_domain + + . = ..() + atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL + atom_storage.max_slots = 1 + atom_storage.max_total_storage = 3 + atom_storage.locked = STORAGE_NOT_LOCKED + icon_state = icon_closed + playsound(src, 'sound/magic/blink.ogg', 50, TRUE) + +/obj/item/storage/lockbox/bitrunning/decrypted/PopulateContents() + var/choice = SSbitrunning.pick_secondary_loot(source_domain) + new choice(src) diff --git a/code/modules/bitrunning/objects/loot_crate.dm b/code/modules/bitrunning/objects/loot_crate.dm index a0a74ecb4b9..db3f927e021 100644 --- a/code/modules/bitrunning/objects/loot_crate.dm +++ b/code/modules/bitrunning/objects/loot_crate.dm @@ -42,11 +42,11 @@ if(isnull(completed_domain)) return - PopulateContents(completed_domain.reward_points, completed_domain.extra_loot, rewards_multiplier) + PopulateContents(completed_domain.reward_points, completed_domain.completion_loot, rewards_multiplier) -/obj/structure/closet/crate/secure/bitrunning/decrypted/PopulateContents(reward_points, list/extra_loot, rewards_multiplier) +/obj/structure/closet/crate/secure/bitrunning/decrypted/PopulateContents(reward_points, list/completion_loot, rewards_multiplier) . = ..() - spawn_loot(extra_loot) + spawn_loot(completion_loot) new /obj/item/stack/ore/iron(src, calculate_loot(reward_points, rewards_multiplier, ORE_MULTIPLIER_IRON)) new /obj/item/stack/ore/glass(src, calculate_loot(reward_points, rewards_multiplier, ORE_MULTIPLIER_GLASS)) @@ -70,16 +70,16 @@ var/random_sum = (rand() + 0.5) * base return ROUND_UP(random_sum * ore_multiplier) -/// Handles spawning extra loot. This tries to handle bad flat and assoc lists -/obj/structure/closet/crate/secure/bitrunning/decrypted/proc/spawn_loot(list/extra_loot) - for(var/path in extra_loot) +/// Handles spawning completion loot. This tries to handle bad flat and assoc lists +/obj/structure/closet/crate/secure/bitrunning/decrypted/proc/spawn_loot(list/completion_loot) + for(var/path in completion_loot) if(!ispath(path)) return FALSE - if(isnull(extra_loot[path])) + if(isnull(completion_loot[path])) return FALSE - for(var/i in 1 to extra_loot[path]) + for(var/i in 1 to completion_loot[path]) new path(src) return TRUE diff --git a/code/modules/bitrunning/objects/quantum_console.dm b/code/modules/bitrunning/objects/quantum_console.dm index 71d7df87f12..6cc8aef6dae 100644 --- a/code/modules/bitrunning/objects/quantum_console.dm +++ b/code/modules/bitrunning/objects/quantum_console.dm @@ -56,7 +56,7 @@ if(isnull(server)) return data - data["available_domains"] = server.get_available_domains() + data["available_domains"] = SSbitrunning.get_available_domains(server.scanner_tier, server.points) data["avatars"] = server.get_avatar_data() return data diff --git a/code/modules/bitrunning/server/_parent.dm b/code/modules/bitrunning/server/_parent.dm index 62e20367190..f3f71376897 100644 --- a/code/modules/bitrunning/server/_parent.dm +++ b/code/modules/bitrunning/server/_parent.dm @@ -20,8 +20,6 @@ var/is_ready = TRUE /// Chance multipled by threat to spawn a glitch var/glitch_chance = 0.05 - /// List of available domains - var/list/available_domains = list() /// Current plugged in users var/list/datum/weakref/avatar_connection_refs = list() /// Cached list of mutable mobs in zone for cybercops @@ -63,13 +61,9 @@ RegisterSignals(src, list(COMSIG_MACHINERY_BROKEN, COMSIG_MACHINERY_POWER_LOST), PROC_REF(on_broken)) RegisterSignal(src, COMSIG_QDELETING, PROC_REF(on_delete)) - // This further gets sorted in the client by cost so it's random and grouped - available_domains = shuffle(subtypesof(/datum/lazy_template/virtual_domain)) - /obj/machinery/quantum_server/Destroy(force) . = ..() - available_domains.Cut() mutation_candidate_refs.Cut() avatar_connection_refs.Cut() spawned_threat_refs.Cut() diff --git a/code/modules/bitrunning/server/loot.dm b/code/modules/bitrunning/server/loot.dm index 0aab2a86ff4..cb4902abfe3 100644 --- a/code/modules/bitrunning/server/loot.dm +++ b/code/modules/bitrunning/server/loot.dm @@ -40,6 +40,15 @@ chosen_forge.start_to_spawn(reward_cache) return TRUE +/obj/machinery/quantum_server/proc/generate_secondary_loot(obj/curiosity, obj/machinery/byteforge/chosen_forge) + spark_at_location(curiosity) // abracadabra! + qdel(curiosity) // and it's gone! + + var/obj/item/storage/lockbox/bitrunning/decrypted/reward_curiosity = new(src, generated_domain) + + chosen_forge.start_to_spawn(reward_curiosity) + return TRUE + /// Returns the markdown text containing domain completion information /obj/machinery/quantum_server/proc/get_completion_certificate() var/base_points = generated_domain.reward_points diff --git a/code/modules/bitrunning/server/map_handling.dm b/code/modules/bitrunning/server/map_handling.dm index 5018e464d6a..2ddff04fc15 100644 --- a/code/modules/bitrunning/server/map_handling.dm +++ b/code/modules/bitrunning/server/map_handling.dm @@ -67,9 +67,9 @@ /// Initializes a new domain if the given key is valid and the user has enough points /obj/machinery/quantum_server/proc/load_domain(map_key) - for(var/datum/lazy_template/virtual_domain/available as anything in subtypesof(/datum/lazy_template/virtual_domain)) - if(map_key == initial(available.key) && points >= initial(available.cost)) - generated_domain = new available() + for(var/datum/lazy_template/virtual_domain/available in SSbitrunning.all_domains) + if(map_key == available.key && points >= available.cost) + generated_domain = available RegisterSignal(generated_domain, COMSIG_LAZY_TEMPLATE_LOADED, PROC_REF(on_template_loaded)) generated_domain.lazy_load() return TRUE @@ -80,6 +80,7 @@ /obj/machinery/quantum_server/proc/load_map_items() var/turf/goal_turfs = list() var/turf/cache_turfs = list() + var/turf/curiosity_turfs = list() for(var/obj/effect/landmark/bitrunning/thing in GLOB.landmarks_list) if(istype(thing, /obj/effect/landmark/bitrunning/hololadder_spawn)) @@ -100,6 +101,11 @@ qdel(thing) continue + if(istype(thing, /obj/effect/landmark/bitrunning/curiosity_spawn)) + curiosity_turfs += get_turf(thing) + qdel(thing) + continue + if(istype(thing, /obj/effect/landmark/bitrunning/loot_signal)) var/turf/signaler_turf = get_turf(thing) signaler_turf.AddComponent(/datum/component/bitrunning_points, generated_domain) @@ -113,6 +119,13 @@ if(!attempt_spawn_cache(cache_turfs)) return FALSE + while(length(curiosity_turfs)) + var/turf/picked_turf = attempt_spawn_curiosity(curiosity_turfs) + if(!picked_turf) + break + generated_domain.secondary_loot_generated += 1 + curiosity_turfs -= picked_turf + return TRUE /// Stops the current virtual domain and disconnects all users @@ -151,6 +164,8 @@ creature.dust(just_ash = TRUE, force = TRUE) // sometimes mobs just don't die + generated_domain.secondary_loot_generated = 0 + avatar_connection_refs.Cut() exit_turfs = list() generated_domain = null diff --git a/code/modules/bitrunning/server/obj_generation.dm b/code/modules/bitrunning/server/obj_generation.dm index 38fee74d4a8..ab17682fb6f 100644 --- a/code/modules/bitrunning/server/obj_generation.dm +++ b/code/modules/bitrunning/server/obj_generation.dm @@ -15,6 +15,26 @@ new /obj/structure/closet/crate/secure/bitrunning/encrypted(chosen_turf) return TRUE +/// Attempts to spawn a lootbox +/obj/machinery/quantum_server/proc/attempt_spawn_curiosity(list/possible_turfs) + if(!length(possible_turfs)) // Out of turfs to place a curiosity + return FALSE + + if(generated_domain.secondary_loot_generated >= assoc_value_sum(generated_domain.secondary_loot)) // Out of curiosities to place + return FALSE + + shuffle_inplace(possible_turfs) + var/turf/chosen_turf = validate_turf(pick(possible_turfs)) + + if(isnull(chosen_turf)) + possible_turfs.Remove(chosen_turf) + chosen_turf = validate_turf(pick(possible_turfs)) + if(isnull(chosen_turf)) + CRASH("vdom: after two attempts, could not find a valid turf for curiosity") + + new /obj/item/storage/lockbox/bitrunning/encrypted(chosen_turf) + return chosen_turf + /// Generates a new avatar for the bitrunner. /obj/machinery/quantum_server/proc/generate_avatar(obj/structure/hololadder/wayout, datum/outfit/netsuit) var/mob/living/carbon/human/avatar = new(wayout.loc) diff --git a/code/modules/bitrunning/server/signal_handlers.dm b/code/modules/bitrunning/server/signal_handlers.dm index 81db7a5b9b0..0e5e949e8bb 100644 --- a/code/modules/bitrunning/server/signal_handlers.dm +++ b/code/modules/bitrunning/server/signal_handlers.dm @@ -47,6 +47,10 @@ generate_loot(arrived, chosen_forge) return + if(istype(arrived, /obj/item/storage/lockbox/bitrunning/encrypted)) + generate_secondary_loot(arrived, chosen_forge, generated_domain) + return + /// Handles examining the server. Shows cooldown time and efficiency. /obj/machinery/quantum_server/proc/on_goal_turf_examined(datum/source, mob/examiner, list/examine_text) SIGNAL_HANDLER diff --git a/code/modules/bitrunning/server/util.dm b/code/modules/bitrunning/server/util.dm index a6069d45e90..24ed78210cf 100644 --- a/code/modules/bitrunning/server/util.dm +++ b/code/modules/bitrunning/server/util.dm @@ -1,4 +1,3 @@ -#define REDACTED "???" #define MAX_DISTANCE 4 // How far crates can spawn from the server /// Resets the cooldown state and updates icons @@ -7,28 +6,6 @@ update_appearance() radio.talk_into(src, "Thermal systems within operational parameters. Proceeding to domain configuration.", RADIO_CHANNEL_SUPPLY) -/// Compiles a list of available domains. -/obj/machinery/quantum_server/proc/get_available_domains() - var/list/levels = list() - - for(var/datum/lazy_template/virtual_domain/domain as anything in available_domains) - if(initial(domain.test_only)) - continue - var/can_view = initial(domain.difficulty) < scanner_tier && initial(domain.cost) <= points + 5 - var/can_view_reward = initial(domain.difficulty) < (scanner_tier + 1) && initial(domain.cost) <= points + 3 - - levels += list(list( - "cost" = initial(domain.cost), - "desc" = can_view ? initial(domain.desc) : "Limited scanning capabilities. Cannot infer domain details.", - "difficulty" = initial(domain.difficulty), - "id" = initial(domain.key), - "is_modular" = initial(domain.is_modular), - "name" = can_view ? initial(domain.name) : REDACTED, - "reward" = can_view_reward ? initial(domain.reward_points) : REDACTED, - )) - - return levels - /// If there are hosted minds, attempts to get a list of their current virtual bodies w/ vitals /obj/machinery/quantum_server/proc/get_avatar_data() var/list/hosted_avatars = list() @@ -120,5 +97,4 @@ if(!tile.is_blocked_turf()) return chosen_turf -#undef REDACTED #undef MAX_DISTANCE diff --git a/code/modules/bitrunning/virtual_domain/domains/abductor_ship.dm b/code/modules/bitrunning/virtual_domain/domains/abductor_ship.dm index 6475a20a0c7..55e6d08a147 100644 --- a/code/modules/bitrunning/virtual_domain/domains/abductor_ship.dm +++ b/code/modules/bitrunning/virtual_domain/domains/abductor_ship.dm @@ -3,7 +3,7 @@ cost = BITRUNNER_COST_MEDIUM desc = "Board an abductor ship and take their goodies." difficulty = BITRUNNER_DIFFICULTY_MEDIUM - extra_loot = list(/obj/item/toy/plush/abductor/agent = 1) + completion_loot = list(/obj/item/toy/plush/abductor/agent = 1) help_text = "An abductor mothership unknowingly entered a hostile environment. \ They are currently preparing to escape the area with their gear and loot including \ the crate. Be careful, they are known for their advanced weaponry." diff --git a/code/modules/bitrunning/virtual_domain/domains/beach_bar.dm b/code/modules/bitrunning/virtual_domain/domains/beach_bar.dm index ae00fc56cf7..80f07448b69 100644 --- a/code/modules/bitrunning/virtual_domain/domains/beach_bar.dm +++ b/code/modules/bitrunning/virtual_domain/domains/beach_bar.dm @@ -1,7 +1,7 @@ /datum/lazy_template/virtual_domain/beach_bar name = "Beach Bar" desc = "A cheerful seaside haven where friendly skeletons serve up drinks. Say, how'd you guys get so dead?" - extra_loot = list(/obj/item/toy/beach_ball = 1) + completion_loot = list(/obj/item/toy/beach_ball = 1) help_text = "This place is running on a skeleton crew, and they don't seem to be too keen to share details. \ Maybe a few drinks of liquid charm will get the spirits up. As the saying goes, if you can't beat 'em, join 'em." key = "beach_bar" diff --git a/code/modules/bitrunning/virtual_domain/domains/bubblegum.dm b/code/modules/bitrunning/virtual_domain/domains/bubblegum.dm index f7f58273d89..ab8b282cfbf 100644 --- a/code/modules/bitrunning/virtual_domain/domains/bubblegum.dm +++ b/code/modules/bitrunning/virtual_domain/domains/bubblegum.dm @@ -3,7 +3,7 @@ cost = BITRUNNER_COST_HIGH desc = "King of the slaughter demons. Bubblegum is a massive, hulking beast with a penchant for violence." difficulty = BITRUNNER_DIFFICULTY_HIGH - extra_loot = list(/obj/item/toy/plush/bubbleplush = 1) + completion_loot = list(/obj/item/toy/plush/bubbleplush = 1) forced_outfit = /datum/outfit/job/miner key = "bubblegum" map_name = "bubblegum" diff --git a/code/modules/bitrunning/virtual_domain/domains/clown_planet.dm b/code/modules/bitrunning/virtual_domain/domains/clown_planet.dm index f1bf4468608..3aff298efdb 100644 --- a/code/modules/bitrunning/virtual_domain/domains/clown_planet.dm +++ b/code/modules/bitrunning/virtual_domain/domains/clown_planet.dm @@ -3,7 +3,7 @@ cost = BITRUNNER_COST_LOW desc = "In the deep, dark reaches of space, there is only Honk." difficulty = BITRUNNER_DIFFICULTY_LOW - extra_loot = list(/obj/item/bikehorn = 1) + completion_loot = list(/obj/item/bikehorn = 1) forced_outfit = /datum/outfit/job/clown help_text = "The trials of the Honkitude have begun. The sound of bike horns wailing in the distance. \ this realm- some sort of puzzle, has existed in legend as the final test of just how silly you are." diff --git a/code/modules/bitrunning/virtual_domain/domains/pipedream.dm b/code/modules/bitrunning/virtual_domain/domains/pipedream.dm index 3eb23b94386..595600ce71c 100644 --- a/code/modules/bitrunning/virtual_domain/domains/pipedream.dm +++ b/code/modules/bitrunning/virtual_domain/domains/pipedream.dm @@ -3,7 +3,7 @@ cost = BITRUNNER_COST_LOW desc = "An abandoned and infested factory manufacturing disposal pipes." difficulty = BITRUNNER_DIFFICULTY_LOW - extra_loot = list(/obj/item/stack/pipe_cleaner_coil/random/five = 1) + completion_loot = list(/obj/item/stack/pipe_cleaner_coil/random/five = 1) help_text = "Not long ago, this place was thriving with activity. The workers \ seemed to have left in a hurry, and now productivity is in the bin. Something \ must have trashed the place, but what?" diff --git a/code/modules/bitrunning/virtual_domain/domains/psyker_zombies.dm b/code/modules/bitrunning/virtual_domain/domains/psyker_zombies.dm index 9cb299055dd..2d4d2c5ee36 100644 --- a/code/modules/bitrunning/virtual_domain/domains/psyker_zombies.dm +++ b/code/modules/bitrunning/virtual_domain/domains/psyker_zombies.dm @@ -4,7 +4,7 @@ desc = "Another neglected corner of the virtual world. This one had to be abandoned due to zombie virus. \ Warning -- Virtual domain does not support visual display. This mission must be completed using echolocation." difficulty = BITRUNNER_DIFFICULTY_MEDIUM - extra_loot = list(/obj/item/radio/headset/psyker = 1) //Looks cool, might make your local burdened chaplain happy. + completion_loot = list(/obj/item/radio/headset/psyker = 1) //Looks cool, might make your local burdened chaplain happy. forced_outfit = /datum/outfit/echolocator help_text = "This once-beloved virtual domain has been corrupted by a virus, rendering it unstable, full of holes, and full of ZOMBIES! \ There should be a Mystery Box nearby to help get you armed. Get armed, and finish what the cyber-police started!" diff --git a/code/modules/bitrunning/virtual_domain/domains/stairs_and_cliffs.dm b/code/modules/bitrunning/virtual_domain/domains/stairs_and_cliffs.dm index 1c79d8fd21d..4d100482429 100644 --- a/code/modules/bitrunning/virtual_domain/domains/stairs_and_cliffs.dm +++ b/code/modules/bitrunning/virtual_domain/domains/stairs_and_cliffs.dm @@ -6,7 +6,8 @@ instead of ladders its stairs and instead of snakes its a steep drop down a \ cliff into rough rocks or liquid plasma." difficulty = BITRUNNER_DIFFICULTY_LOW - extra_loot = list(/obj/item/clothing/suit/costume/snowman = 2) + completion_loot = list(/obj/item/clothing/suit/costume/snowman = 2) + secondary_loot = list(/obj/item/clothing/shoes/wheelys/skishoes = 2, /obj/item/clothing/head/costume/ushanka/polar = 1) forced_outfit = /datum/outfit/job/virtual_domain_iceclimber key = "stairs_and_cliffs" map_name = "stairs_and_cliffs" diff --git a/code/modules/bitrunning/virtual_domain/domains/syndicate_assault.dm b/code/modules/bitrunning/virtual_domain/domains/syndicate_assault.dm index 24fa6e5482a..5f754dd433a 100644 --- a/code/modules/bitrunning/virtual_domain/domains/syndicate_assault.dm +++ b/code/modules/bitrunning/virtual_domain/domains/syndicate_assault.dm @@ -3,7 +3,7 @@ cost = BITRUNNER_COST_MEDIUM desc = "Board the enemy ship and recover the stolen cargo." difficulty = BITRUNNER_DIFFICULTY_MEDIUM - extra_loot = list(/obj/item/toy/plush/nukeplushie = 1) + completion_loot = list(/obj/item/toy/plush/nukeplushie = 1) help_text = "A group of Syndicate operatives have stolen valuable cargo from the station. \ They have boarded their ship and are attempting to escape. Infiltrate their ship and recover \ the crate. Be careful, they are extremely armed." diff --git a/code/modules/bitrunning/virtual_domain/domains/vaporwave.dm b/code/modules/bitrunning/virtual_domain/domains/vaporwave.dm index b704441edaa..5da6449ccf9 100644 --- a/code/modules/bitrunning/virtual_domain/domains/vaporwave.dm +++ b/code/modules/bitrunning/virtual_domain/domains/vaporwave.dm @@ -3,7 +3,7 @@ cost = BITRUNNER_COST_EXTREME desc = "Suspended in the silent void of space, the Neon Relic is a haunting echo of a retro-futuristic era. Hang out, enjoy the view." difficulty = BITRUNNER_DIFFICULTY_NONE - extra_loot = list(/obj/item/stack/spacecash/c500 = 4) + completion_loot = list(/obj/item/stack/spacecash/c500 = 4) key = "vaporwave" map_name = "vaporwave" reward_points = BITRUNNER_REWARD_EXTREME diff --git a/code/modules/bitrunning/virtual_domain/domains/xeno_nest.dm b/code/modules/bitrunning/virtual_domain/domains/xeno_nest.dm index ed708d0592c..6b76956eacc 100644 --- a/code/modules/bitrunning/virtual_domain/domains/xeno_nest.dm +++ b/code/modules/bitrunning/virtual_domain/domains/xeno_nest.dm @@ -3,7 +3,7 @@ cost = BITRUNNER_COST_LOW desc = "Our ship scanners have detected lifeforms of unknown origin. Friendly attempts to contact them have failed." difficulty = BITRUNNER_DIFFICULTY_LOW - extra_loot = list(/obj/item/toy/plush/rouny = 1) + completion_loot = list(/obj/item/toy/plush/rouny = 1) help_text = "You are on a barren planet filled with hostile creatures. There is a crate here, not hidden, \ simply protected. Expect resistance." is_modular = TRUE diff --git a/code/modules/bitrunning/virtual_domain/virtual_domain.dm b/code/modules/bitrunning/virtual_domain/virtual_domain.dm index 41e5da8973e..b316bb97cae 100644 --- a/code/modules/bitrunning/virtual_domain/virtual_domain.dm +++ b/code/modules/bitrunning/virtual_domain/virtual_domain.dm @@ -29,7 +29,11 @@ /// Byond will look for modular mob segment landmarks then choose from here at random. You can make them unique also. var/list/datum/modular_mob_segment/mob_modules = list() /// An assoc list of typepath/amount to spawn on completion. Not weighted - the value is the amount - var/list/extra_loot + var/list/completion_loot + /// An accoc list of typepath/amount to spawn from secondary objectives. Not weighted - the value is the total number of items that can be obtained. + var/list/secondary_loot = list() + /// Number of secondary loot boxes generated. Resets when the domain is reloaded. + var/secondary_loot_generated /// Forces all mob modules to only load once var/modular_unique_mobs = FALSE // Name to show in the UI diff --git a/code/modules/clothing/head/hat.dm b/code/modules/clothing/head/hat.dm index 1b65af66d33..33e110efba8 100644 --- a/code/modules/clothing/head/hat.dm +++ b/code/modules/clothing/head/hat.dm @@ -313,6 +313,13 @@ to_chat(user, span_notice("You lower the ear flaps on the ushanka.")) earflaps = !earflaps +/obj/item/clothing/head/costume/ushanka/polar + name = "bear hunter's ushanka" + desc = "Handcrafted in Siberia from real polar bears." + icon_state = "ushankadown_polar" + upsprite = "ushankaup_polar" + downsprite = "ushankadown_polar" + /obj/item/clothing/head/costume/nightcap/blue name = "blue nightcap" desc = "A blue nightcap for all the dreamers and snoozers out there." diff --git a/code/modules/mob/living/basic/space_fauna/bear/_bear.dm b/code/modules/mob/living/basic/space_fauna/bear/_bear.dm index e23e022d00e..eef08b30197 100644 --- a/code/modules/mob/living/basic/space_fauna/bear/_bear.dm +++ b/code/modules/mob/living/basic/space_fauna/bear/_bear.dm @@ -82,6 +82,12 @@ icon_dead = "snowbear_dead" desc = "It's a polar bear, in space, but not actually in space." +/mob/living/basic/bear/snow/ancient + name = "ancient polar bear" + desc = "A grizzled old polar bear, its hide thick enough to make it impervious to almost all weapons." + status_flags = CANPUSH | GODMODE + gold_core_spawnable = NO_SPAWN + /mob/living/basic/bear/snow/Initialize(mapload) . = ..() ADD_TRAIT(src, TRAIT_SNOWSTORM_IMMUNE, INNATE_TRAIT) diff --git a/code/modules/unit_tests/bitrunning.dm b/code/modules/unit_tests/bitrunning.dm index 568eeeed8c1..44dd69390af 100644 --- a/code/modules/unit_tests/bitrunning.dm +++ b/code/modules/unit_tests/bitrunning.dm @@ -9,7 +9,7 @@ TEST_ASSERT_NOTNULL(vdom.key, "[path] should have a key") TEST_ASSERT_NOTNULL(vdom.map_name, "[path] should have a map name") - if(!length(vdom.extra_loot)) + if(!length(vdom.completion_loot)) continue - TEST_ASSERT_EQUAL(cache.spawn_loot(vdom.extra_loot), TRUE, "[path] didn't spawn loot. Extra loot should be an associative list") + TEST_ASSERT_EQUAL(cache.spawn_loot(vdom.completion_loot), TRUE, "[path] didn't spawn loot. Completion loot should be an associative list") diff --git a/icons/mob/clothing/head/costume.dmi b/icons/mob/clothing/head/costume.dmi index cddd1f7148d..4bce495a291 100644 Binary files a/icons/mob/clothing/head/costume.dmi and b/icons/mob/clothing/head/costume.dmi differ diff --git a/icons/mob/inhands/equipment/briefcase_lefthand.dmi b/icons/mob/inhands/equipment/briefcase_lefthand.dmi index 5781ecdc992..9eb7d24a3ce 100644 Binary files a/icons/mob/inhands/equipment/briefcase_lefthand.dmi and b/icons/mob/inhands/equipment/briefcase_lefthand.dmi differ diff --git a/icons/mob/inhands/equipment/briefcase_righthand.dmi b/icons/mob/inhands/equipment/briefcase_righthand.dmi index d48529a54e9..06ed8e2c8ec 100644 Binary files a/icons/mob/inhands/equipment/briefcase_righthand.dmi and b/icons/mob/inhands/equipment/briefcase_righthand.dmi differ diff --git a/icons/obj/clothing/head/costume.dmi b/icons/obj/clothing/head/costume.dmi index 9344243d723..e72c278b6eb 100644 Binary files a/icons/obj/clothing/head/costume.dmi and b/icons/obj/clothing/head/costume.dmi differ diff --git a/icons/obj/storage/case.dmi b/icons/obj/storage/case.dmi index 0320ce881f9..a47c86eea9f 100644 Binary files a/icons/obj/storage/case.dmi and b/icons/obj/storage/case.dmi differ diff --git a/modular_skyrat/modules/bitrunning/code/virtual_domains/ancient_milsim/virtual_domain.dm b/modular_skyrat/modules/bitrunning/code/virtual_domains/ancient_milsim/virtual_domain.dm index 9d20d537ed9..a6d0843cb16 100644 --- a/modular_skyrat/modules/bitrunning/code/virtual_domains/ancient_milsim/virtual_domain.dm +++ b/modular_skyrat/modules/bitrunning/code/virtual_domains/ancient_milsim/virtual_domain.dm @@ -4,7 +4,7 @@ cost = BITRUNNER_COST_HIGH desc = "Recreate the events of the Border War's 'hot' part-long-gone as a Solarian strike team sweeping CIN compounds; sponsored by the SolFed recreation enthusiasts. \ Multiplayer playthrough and proper preparation highly recommended." - extra_loot = list(/obj/item/bitrunning_disk/item/ancient_milsim = 1) + completion_loot = list(/obj/item/bitrunning_disk/item/ancient_milsim = 1) difficulty = BITRUNNER_DIFFICULTY_HIGH help_text = "The last part of this domain has a chance to be very PvP-centric. It's best if you don't come alone, and smuggle some ability and gear disks." forced_outfit = /datum/outfit/solfed_bitrun diff --git a/tgstation.dme b/tgstation.dme index 8cf1bb15132..3d4cf38d3af 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -710,6 +710,7 @@ #include "code\controllers\subsystem\atoms.dm" #include "code\controllers\subsystem\augury.dm" #include "code\controllers\subsystem\ban_cache.dm" +#include "code\controllers\subsystem\bitrunning.dm" #include "code\controllers\subsystem\blackbox.dm" #include "code\controllers\subsystem\blackmarket.dm" #include "code\controllers\subsystem\chat.dm" @@ -3515,6 +3516,7 @@ #include "code\modules\bitrunning\objects\hololadder.dm" #include "code\modules\bitrunning\objects\host_monitor.dm" #include "code\modules\bitrunning\objects\landmarks.dm" +#include "code\modules\bitrunning\objects\loot_box.dm" #include "code\modules\bitrunning\objects\loot_crate.dm" #include "code\modules\bitrunning\objects\netpod.dm" #include "code\modules\bitrunning\objects\quantum_console.dm" diff --git a/tgui/packages/tgui/interfaces/QuantumConsole.tsx b/tgui/packages/tgui/interfaces/QuantumConsole.tsx index dca8f2dbf15..7d64056307c 100644 --- a/tgui/packages/tgui/interfaces/QuantumConsole.tsx +++ b/tgui/packages/tgui/interfaces/QuantumConsole.tsx @@ -50,6 +50,7 @@ type Domain = { difficulty: number; id: string; is_modular: BooleanLike; + has_secondary_objectives: BooleanLike; name: string; reward: number | string; }; @@ -228,7 +229,16 @@ const AccessView = (props) => { const DomainEntry = (props: DomainEntryProps) => { const { - domain: { cost, desc, difficulty, id, is_modular, name, reward }, + domain: { + cost, + desc, + difficulty, + id, + is_modular, + has_secondary_objectives, + name, + reward, + }, } = props; const { act, data } = useBackend(); if (!isConnected(data)) { @@ -268,6 +278,9 @@ const DomainEntry = (props: DomainEntryProps) => { <> {name} {!!is_modular && name !== '???' && } + {!!has_secondary_objectives && name !== '???' && ( + + )} } > @@ -275,6 +288,7 @@ const DomainEntry = (props: DomainEntryProps) => { {desc} {!!is_modular && ' (Modular)'} + {!!has_secondary_objectives && ' (Secondary Objective Available)'}