mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-25 08:34:23 +00:00
## About The Pull Request
- Large amount of storage datum cleanup.
- Documentation.
- Maybe more consistent use of parent vs real_location.
- Removes the weakrefs, replaces it with just references.
- These were already managed references anyways so why bother?
- Removes a bunch of arguments no one used and would ever used so only
the most useful args are left.
- Some bugfixes.
## Why It's Good For The Game
Aiming to make storage easier to work with. The whole intent of this was
to bugfix the whole "weight class" thing that keeps popping up but I had
to do this first.
## Changelog
🆑 Melbert
fix: When placing an item into storage (such as backpacks), all nearby
mobs now get a message, rather than just the first mob.
fix: TGC decks of cards should act a bit less odd when looking inside.
refactor: Refactored a bit of storage, cleaned up a fair bit of its
code. Let me know if you notice anything funky about storage (like
backpacks).
/🆑
484 lines
16 KiB
Plaintext
484 lines
16 KiB
Plaintext
/// How many lines of log we keep
|
|
#define EXODRONE_LOG_SIZE 15
|
|
/// Size of drone storage shared between loot and tools.
|
|
#define EXODRONE_CARGO_SLOTS 6
|
|
|
|
// Fuel types and travel time per unit of distance on that fuel.
|
|
#define FUEL_BASIC "basic"
|
|
#define BASIC_FUEL_TIME_COST 250
|
|
|
|
#define FUEL_ADVANCED "advanced"
|
|
#define ADVANCED_FUEL_TIME_COST 200
|
|
|
|
#define FUEL_EXOTIC "exotic"
|
|
#define EXOTIC_FUEL_TIME_COST 100
|
|
|
|
/// All exodrones.
|
|
GLOBAL_LIST_EMPTY(exodrones)
|
|
/// All exodrone launchers.
|
|
GLOBAL_LIST_EMPTY(exodrone_launchers)
|
|
|
|
/// Exploration drone
|
|
/obj/item/exodrone
|
|
name = "exploration drone"
|
|
desc = "A long range, semi-autonomous exploration drone."
|
|
icon = 'icons/obj/exploration.dmi'
|
|
icon_state = "drone"
|
|
w_class = WEIGHT_CLASS_BULKY
|
|
|
|
/// Current drone status, see code\__DEFINES\adventure.dm
|
|
var/drone_status = EXODRONE_IDLE
|
|
/// Are we currently controlled by remote terminal, blocks other terminals from interacting with this drone.
|
|
var/controlled = FALSE
|
|
/// Site we're currently at, null means station.
|
|
var/datum/exploration_site/location
|
|
/// Site we're currently travelling to, null means going back to station - check drone status if you want to check if traveling or idle
|
|
var/datum/exploration_site/travel_target
|
|
/// Total travel time to our current target
|
|
var/travel_time
|
|
/// Id of travel timer
|
|
var/travel_timer_id
|
|
/// Message that will show up on busy screen
|
|
var/busy_message = "Doing something..."
|
|
/// When we entered busy state
|
|
var/busy_start_time
|
|
/// How long will busy state last
|
|
var/busy_duration
|
|
// Our current adventure if any.
|
|
var/datum/adventure/current_adventure
|
|
// Our current simple event ui data if any
|
|
var/list/current_event_ui_data
|
|
/// Pad we've launched from, we'll try to land on this one first when coming back if it still exists.
|
|
var/datum/weakref/last_pad
|
|
/// Log of recent events
|
|
var/list/drone_log = list()
|
|
/// List of tools, EXODRONE_TOOL_WELDER etc
|
|
var/list/tools = list()
|
|
// Current travel cost per 1 distance in deciseconds
|
|
var/travel_cost_coeff = BASIC_FUEL_TIME_COST
|
|
/// Repeated drone name counter
|
|
var/static/name_counter = list()
|
|
/// Used to provide source to the regex replacement function. DO NOT MODIFY DIRECTLY
|
|
var/static/obj/item/exodrone/_regex_context
|
|
|
|
/obj/item/exodrone/Initialize(mapload)
|
|
. = ..()
|
|
name = pick(strings(EXODRONE_FILE,"probe_names"))
|
|
if(name_counter[name])
|
|
name_counter[name]++
|
|
name = "[name] \Roman[name_counter[name]]"
|
|
else
|
|
name_counter[name] = 1
|
|
GLOB.exodrones += src
|
|
// Cargo storage
|
|
create_storage(max_slots = EXODRONE_CARGO_SLOTS, canthold = GLOB.blacklisted_cargo_types)
|
|
|
|
/obj/item/exodrone/Destroy()
|
|
. = ..()
|
|
GLOB.exodrones -= src
|
|
|
|
/// Description for drone listing, describes location and current status
|
|
/obj/item/exodrone/proc/ui_description()
|
|
switch(drone_status)
|
|
if(EXODRONE_TRAVEL)
|
|
return travel_target ? "Traveling to [travel_target.display_name()]." : "Traveling back to station."
|
|
if(EXODRONE_EXPLORATION, EXODRONE_ADVENTURE, EXODRONE_BUSY)
|
|
return "Exploring [location?.display_name() || "ERROR"]." // better safe than sorry.
|
|
if(EXODRONE_IDLE)
|
|
return "Idle."
|
|
|
|
// Searches for blacklisted items hidden inside containers
|
|
/obj/item/exodrone/proc/check_blacklist()
|
|
for(var/obj/item/contained_item in contents)
|
|
if(contained_item.contents)
|
|
for(var/subcontained_object in contained_item.get_all_contents())
|
|
if(is_type_in_typecache(subcontained_object, GLOB.blacklisted_cargo_types))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/// Starts travel for site, does not validate if it's possible
|
|
/obj/item/exodrone/proc/launch_for(datum/exploration_site/target_site)
|
|
if(!location) //We're launching from station, fuel up
|
|
var/obj/machinery/exodrone_launcher/pad = locate() in loc
|
|
pad.fuel_up(src)
|
|
pad.launch_effect()
|
|
last_pad = WEAKREF(pad)
|
|
drone_log("Launched from [pad.name] and set course for [target_site.display_name()]")
|
|
else
|
|
drone_log("Launched from [location.display_name()] and set course for [target_site ? target_site.display_name() : station_name()]")
|
|
set_status(EXODRONE_TRAVEL)
|
|
moveToNullspace()
|
|
var/distance_to_travel = target_site ? target_site.distance : location.distance //If we're going home distance is distance of our current location
|
|
if(location && target_site) //Traveling site to site is faster (don't think too hard on 3d space logistics here)
|
|
distance_to_travel = max(abs(target_site.distance - location.distance),1)
|
|
travel_target = target_site
|
|
travel_time = travel_cost_coeff*distance_to_travel
|
|
travel_timer_id = addtimer(CALLBACK(src, PROC_REF(finish_travel)),travel_time,TIMER_STOPPABLE)
|
|
|
|
/// Travel cleanup
|
|
/obj/item/exodrone/proc/finish_travel()
|
|
location = travel_target
|
|
travel_timer_id = null
|
|
travel_time = null
|
|
if(location)//We're arriving at exploration site
|
|
location.on_drone_arrival(src)
|
|
set_status(EXODRONE_EXPLORATION)
|
|
else
|
|
var/obj/machinery/exodrone_launcher = find_landing_pad()
|
|
if(exodrone_launcher)
|
|
forceMove(get_turf(exodrone_launcher))
|
|
drone_log("Arrived at [station_name()]. Landing at [exodrone_launcher].")
|
|
else
|
|
var/turf/drop_zone = drop_somewhere_on_station()
|
|
drone_log("Arrived at [station_name()]. Emergency landing at [drop_zone.loc.name].")
|
|
set_status(EXODRONE_IDLE)
|
|
|
|
/obj/item/exodrone/proc/set_status(new_status)
|
|
SEND_SIGNAL(src,COMSIG_EXODRONE_STATUS_CHANGED)
|
|
drone_status = new_status
|
|
|
|
/// Cargo space left
|
|
/obj/item/exodrone/proc/space_left()
|
|
return EXODRONE_CARGO_SLOTS - length(contents) - length(tools)
|
|
|
|
/// Adds drone tool and resizes storage.
|
|
/obj/item/exodrone/proc/add_tool(tool_type)
|
|
if(space_left() > 0 && (tool_type in GLOB.exodrone_tool_metadata))
|
|
tools += tool_type
|
|
update_storage_size()
|
|
|
|
/// Removes drone tool and resizes storage.
|
|
/obj/item/exodrone/proc/remove_tool(tool_type)
|
|
tools -= tool_type
|
|
update_storage_size()
|
|
|
|
/// Resizes storage component depending on slots used by tools.
|
|
/obj/item/exodrone/proc/update_storage_size()
|
|
atom_storage.max_slots = EXODRONE_CARGO_SLOTS - length(tools)
|
|
|
|
/// Builds ui data for drone storage.
|
|
/obj/item/exodrone/proc/get_cargo_data()
|
|
. = list()
|
|
for(var/tool in tools)
|
|
. += list(list("type"="tool","name"=tool))
|
|
for(var/obj/cargo in contents)
|
|
. += list(list("type"="cargo","name"=cargo.name, "ref"=ref(cargo)))
|
|
for(var/_ in 1 to space_left())
|
|
. += list(list("type"="empty","name"="Free space"))
|
|
|
|
/// Tries to add loot to drone cargo while respecting space left
|
|
/obj/item/exodrone/proc/try_transfer(obj/loot, delete_on_failure=TRUE)
|
|
if(space_left())
|
|
loot.forceMove(src)
|
|
drone_log("Acquired [loot.name].")
|
|
else
|
|
drone_log("Abandoned [loot.name] due to lack of space.")
|
|
if(delete_on_failure)
|
|
qdel(loot)
|
|
|
|
/// Crashes the drone somewhere random if there's no launchpad to be found.
|
|
/obj/item/exodrone/proc/drop_somewhere_on_station()
|
|
var/turf/random_spot = get_safe_random_station_turf()
|
|
|
|
var/obj/structure/closet/supplypod/pod = podspawn(list(
|
|
"target" = random_spot,
|
|
))
|
|
forceMove(pod)
|
|
return random_spot
|
|
|
|
/// Tries to find landing pad, starting with the one we launched from.
|
|
/obj/item/exodrone/proc/find_landing_pad()
|
|
var/obj/machinery/exodrone_launcher/landing_pad = last_pad?.resolve()
|
|
if(landing_pad)
|
|
return landing_pad
|
|
for(var/obj/machinery/exodrone_launcher/other_pad in GLOB.exodrone_launchers)
|
|
return other_pad
|
|
|
|
/// encounters random or specificed event for the current site.
|
|
/obj/item/exodrone/proc/explore_site(datum/exploration_event/specific_event)
|
|
if(!specific_event) //encounter random event
|
|
var/list/events_to_encounter = list()
|
|
for(var/datum/exploration_event/event in location.events)
|
|
if(event.visited)
|
|
continue
|
|
events_to_encounter += event
|
|
if(!length(events_to_encounter))
|
|
drone_log("It seems there's nothing interesting left around [location.name].")
|
|
return
|
|
var/datum/exploration_event/encountered_event = pick(events_to_encounter)
|
|
encountered_event.encounter(src)
|
|
else if(specific_event.is_targetable())
|
|
specific_event.encounter(src)
|
|
|
|
/obj/item/exodrone/proc/get_adventure_data()
|
|
var/list/data = current_adventure?.ui_data()
|
|
data["description"] = updateKeywords(data["description"])
|
|
var/list/choices = data["choices"]
|
|
for(var/list/choice in choices)
|
|
choice["text"] = updateKeywords(choice["text"])
|
|
return data
|
|
|
|
///Replaces $$SITE_NAME with site name and $$QualityName with quality values
|
|
/obj/item/exodrone/proc/updateKeywords(text)
|
|
_regex_context = src
|
|
var/static/regex/keywordRegex = regex(@"\$\$(\S*)","g")
|
|
. = keywordRegex.Replace(text, /obj/item/exodrone/proc/replace_keyword)
|
|
_regex_context = null
|
|
|
|
/// This is called with src = regex datum, so don't try to access any instance variables directly here.
|
|
/obj/item/exodrone/proc/replace_keyword(match,g1)
|
|
REGEX_REPLACE_HANDLER
|
|
switch(g1)
|
|
if("SITE_NAME")
|
|
return _regex_context.location.display_name()
|
|
else
|
|
if(_regex_context.current_adventure.qualities[g1])
|
|
return "[_regex_context.current_adventure.qualities[g1]]"
|
|
else
|
|
return ""
|
|
|
|
/obj/item/exodrone/proc/start_adventure(datum/adventure/adventure)
|
|
current_adventure = adventure
|
|
RegisterSignal(current_adventure,COMSIG_ADVENTURE_FINISHED, PROC_REF(resolve_adventure))
|
|
RegisterSignal(current_adventure,COMSIG_ADVENTURE_QUALITY_INIT, PROC_REF(add_tool_qualities))
|
|
RegisterSignal(current_adventure,COMSIG_ADVENTURE_DELAY_START, PROC_REF(adventure_delay_start))
|
|
RegisterSignal(current_adventure,COMSIG_ADVENTURE_DELAY_END, PROC_REF(adventure_delay_end))
|
|
set_status(EXODRONE_ADVENTURE)
|
|
current_adventure.start_adventure()
|
|
|
|
/// Handles finishing adventure
|
|
/obj/item/exodrone/proc/resolve_adventure(datum/source,result)
|
|
SIGNAL_HANDLER
|
|
switch(result)
|
|
if(ADVENTURE_RESULT_SUCCESS)
|
|
award_adventure_loot()
|
|
UnregisterSignal(current_adventure,list(COMSIG_ADVENTURE_FINISHED,COMSIG_ADVENTURE_QUALITY_INIT,COMSIG_ADVENTURE_DELAY_START,COMSIG_ADVENTURE_DELAY_END))
|
|
current_adventure = null
|
|
set_status(EXODRONE_EXPLORATION)
|
|
return
|
|
if(ADVENTURE_RESULT_DAMAGE)
|
|
damage(max_integrity*0.5) //Half health lost
|
|
if(!QDELETED(src)) // Don't bother if we just blown up from the damage
|
|
UnregisterSignal(current_adventure,list(COMSIG_ADVENTURE_FINISHED,COMSIG_ADVENTURE_QUALITY_INIT,COMSIG_ADVENTURE_DELAY_START,COMSIG_ADVENTURE_DELAY_END))
|
|
current_adventure = null
|
|
set_status(EXODRONE_EXPLORATION)
|
|
return
|
|
if(ADVENTURE_RESULT_DEATH)
|
|
qdel(src)
|
|
|
|
/// Adds loot from current adventure to the drone
|
|
/obj/item/exodrone/proc/award_adventure_loot()
|
|
if(length(current_adventure.loot_categories))
|
|
var/generator_type = GLOB.adventure_loot_generator_index[pick(current_adventure.loot_categories)]
|
|
if(!generator_type)
|
|
return //Could probably warn but i suppose this is up to adventure creator.
|
|
var/datum/adventure_loot_generator/generator = new generator_type
|
|
generator.transfer_loot(src)
|
|
|
|
/// Applies adventure qualities based on our tools
|
|
/obj/item/exodrone/proc/add_tool_qualities(datum/source,list/quality_list)
|
|
SIGNAL_HANDLER
|
|
for(var/tool in tools)
|
|
quality_list[tool] = 1
|
|
|
|
/obj/item/exodrone/proc/adventure_delay_start(datum/source, delay_time,delay_message)
|
|
SIGNAL_HANDLER
|
|
set_busy(delay_message,delay_time)
|
|
|
|
/obj/item/exodrone/proc/adventure_delay_end(datum/source)
|
|
SIGNAL_HANDLER
|
|
unset_busy(EXODRONE_ADVENTURE)
|
|
|
|
/// Enters busy mode for a given duration.
|
|
/obj/item/exodrone/proc/set_busy(message,duration)
|
|
if(message)
|
|
busy_message = message
|
|
busy_start_time = world.time
|
|
busy_duration = duration
|
|
set_status(EXODRONE_BUSY)
|
|
|
|
/// Resets busy status
|
|
/obj/item/exodrone/proc/unset_busy(new_status)
|
|
busy_message = initial(busy_message)
|
|
busy_start_time = null
|
|
busy_duration = null
|
|
set_status(new_status)
|
|
|
|
/obj/item/exodrone/proc/busy_time_left()
|
|
return busy_duration - (world.time - busy_start_time)
|
|
|
|
/// Returns failure message or FALSE if we're ready to travel
|
|
/obj/item/exodrone/proc/travel_error()
|
|
/// We're home and on ready pad or exploring and out of any events/adventures
|
|
switch(drone_status)
|
|
if(EXODRONE_IDLE)
|
|
var/obj/machinery/exodrone_launcher/pad = locate() in loc
|
|
if(!pad)
|
|
return "No launcher"
|
|
if(!pad.fuel_canister)
|
|
return "No fuel in launcher"
|
|
if(pad.fuel_canister.uses <= 0)
|
|
return "Launcher fuel used up"
|
|
return FALSE
|
|
if(EXODRONE_EXPLORATION)
|
|
if(current_event_ui_data)
|
|
return "Busy"
|
|
return FALSE
|
|
else
|
|
return ""
|
|
|
|
/// Deals damage in adventures/events.
|
|
/obj/item/exodrone/proc/damage(amount)
|
|
take_damage(amount)
|
|
drone_log("Sustained [amount] damage.")
|
|
|
|
/obj/item/exodrone/proc/drone_log(message)
|
|
if(length(drone_log) > EXODRONE_LOG_SIZE)
|
|
drone_log = list()
|
|
drone_log.Insert(1,message)
|
|
|
|
/obj/item/exodrone/proc/has_tool(tool_type)
|
|
return tools.Find(tool_type)
|
|
|
|
/// Exploration drone launcher
|
|
/obj/machinery/exodrone_launcher
|
|
name = "exploration drone launcher"
|
|
desc = "A launch pad designed to send exploration drones into the great beyond."
|
|
icon = 'icons/obj/exploration.dmi'
|
|
icon_state = "launcher"
|
|
/// Loaded fuel pellet.
|
|
var/obj/item/fuel_pellet/fuel_canister
|
|
|
|
/obj/machinery/exodrone_launcher/Initialize(mapload)
|
|
. = ..()
|
|
GLOB.exodrone_launchers += src
|
|
|
|
/obj/machinery/exodrone_launcher/examine(user)
|
|
. = ..()
|
|
if(fuel_canister)
|
|
. += span_notice("You can remove the [fuel_canister] with a <b>prying tool</b>.")
|
|
|
|
/obj/machinery/exodrone_launcher/attackby(obj/item/weapon, mob/living/user, params)
|
|
if(istype(weapon, /obj/item/fuel_pellet))
|
|
if(fuel_canister)
|
|
to_chat(user, span_warning("There's already fuel loaded inside [src]!"))
|
|
return TRUE
|
|
if(!user.transferItemToLoc(weapon, src))
|
|
return
|
|
fuel_canister = weapon
|
|
update_icon()
|
|
return TRUE
|
|
|
|
if(istype(weapon, /obj/item/exodrone) && user.transferItemToLoc(weapon, drop_location()))
|
|
return TRUE
|
|
|
|
return ..()
|
|
|
|
/obj/machinery/exodrone_launcher/crowbar_act(mob/living/user, obj/item/crowbar)
|
|
if(!fuel_canister)
|
|
return
|
|
|
|
to_chat(user, span_notice("You remove [fuel_canister] from [src]."))
|
|
fuel_canister.forceMove(drop_location())
|
|
fuel_canister = null
|
|
update_icon()
|
|
return TRUE
|
|
|
|
/obj/machinery/exodrone_launcher/Destroy()
|
|
GLOB.exodrone_launchers -= src
|
|
QDEL_NULL(fuel_canister)
|
|
return ..()
|
|
|
|
/obj/machinery/exodrone_launcher/update_overlays()
|
|
. = ..()
|
|
if(fuel_canister && fuel_canister.uses > 0)
|
|
switch(fuel_canister.fuel_type)
|
|
if(FUEL_BASIC)
|
|
. += "launchpad_fuel_basic"
|
|
if(FUEL_ADVANCED)
|
|
. += "launchpad_fuel_advanced"
|
|
if(FUEL_EXOTIC)
|
|
. += "launchpad_fuel_exotic"
|
|
|
|
/*
|
|
* Gets the fuel travel coefficient for what type of fuel is within the launcher.
|
|
*/
|
|
/obj/machinery/exodrone_launcher/proc/get_fuel_coefficent()
|
|
if(!fuel_canister)
|
|
return
|
|
switch(fuel_canister.fuel_type)
|
|
if(FUEL_BASIC)
|
|
return BASIC_FUEL_TIME_COST
|
|
if(FUEL_ADVANCED)
|
|
return ADVANCED_FUEL_TIME_COST
|
|
if(FUEL_EXOTIC)
|
|
return EXOTIC_FUEL_TIME_COST
|
|
|
|
/*
|
|
* Use up some of the fuel within the launcher to power the drone.
|
|
*
|
|
* drone - the drone that's being fuelled by our launcher.
|
|
*/
|
|
/obj/machinery/exodrone_launcher/proc/fuel_up(obj/item/exodrone/drone)
|
|
drone.travel_cost_coeff = get_fuel_coefficent()
|
|
fuel_canister.use()
|
|
update_icon()
|
|
|
|
/*
|
|
* Plays an effect on the pad, with a sound effect to boot.
|
|
*/
|
|
/obj/machinery/exodrone_launcher/proc/launch_effect()
|
|
playsound(src,'sound/effects/podwoosh.ogg',50, FALSE)
|
|
do_smoke(1, holder = src, location = get_turf(src))
|
|
|
|
/obj/machinery/exodrone_launcher/Exited(atom/movable/gone, direction)
|
|
. = ..()
|
|
if(gone == fuel_canister)
|
|
fuel_canister = null
|
|
update_icon()
|
|
|
|
/obj/item/exodrone/proc/get_travel_coeff()
|
|
switch(drone_status)
|
|
if(EXODRONE_IDLE)
|
|
var/obj/machinery/exodrone_launcher/pad = locate() in loc
|
|
if(pad && pad.fuel_canister)
|
|
return pad.get_fuel_coefficent()
|
|
else
|
|
return travel_cost_coeff
|
|
else
|
|
return travel_cost_coeff
|
|
|
|
/obj/item/fuel_pellet
|
|
name = "standard fuel pellet"
|
|
desc = "A compressed fuel pellet for long-distance drone flight."
|
|
icon = 'icons/obj/exploration.dmi'
|
|
icon_state = "fuel_basic"
|
|
/// The type of fuel this pellet has within.
|
|
var/fuel_type = FUEL_BASIC
|
|
/// The amount of uses left in this fuel pellet.
|
|
var/uses = 5
|
|
|
|
/obj/item/fuel_pellet/use(used)
|
|
uses--
|
|
if(uses <= 0)
|
|
qdel(src)
|
|
|
|
/obj/item/fuel_pellet/advanced
|
|
name = "advanced fuel pellet"
|
|
fuel_type = FUEL_ADVANCED
|
|
icon_state = "fuel_advanced"
|
|
|
|
/obj/item/fuel_pellet/exotic
|
|
name = "exotic fuel pellet"
|
|
fuel_type = FUEL_EXOTIC
|
|
icon_state = "fuel_exotic"
|
|
|
|
#undef EXODRONE_LOG_SIZE
|
|
#undef EXODRONE_CARGO_SLOTS
|
|
#undef FUEL_BASIC
|
|
#undef BASIC_FUEL_TIME_COST
|
|
#undef FUEL_ADVANCED
|
|
#undef ADVANCED_FUEL_TIME_COST
|
|
#undef FUEL_EXOTIC
|
|
#undef EXOTIC_FUEL_TIME_COST
|