diff --git a/_maps/map_files/generic/centcomm.dmm b/_maps/map_files/generic/centcomm.dmm index f21ccb4e85ca..95e4b0265056 100644 --- a/_maps/map_files/generic/centcomm.dmm +++ b/_maps/map_files/generic/centcomm.dmm @@ -5410,7 +5410,6 @@ /turf/simulated/floor/mineral/plastitanium/red, /area/shuttle/assault_pod) "sh" = ( -/obj/effect/spawner/window/reinforced, /obj/machinery/door/poddoor/shutters{ density = 0; dir = 2; @@ -5419,6 +5418,7 @@ name = "Privacy Shutters"; opacity = 0 }, +/obj/effect/spawner/window/shuttle, /turf/simulated/floor/mineral/titanium/blue, /area/shuttle/trade/sol) "sj" = ( diff --git a/_maps/map_files/stations/boxstation.dmm b/_maps/map_files/stations/boxstation.dmm index 412826b1377c..e04d48bfe0af 100644 --- a/_maps/map_files/stations/boxstation.dmm +++ b/_maps/map_files/stations/boxstation.dmm @@ -11923,7 +11923,6 @@ /area/station/hallway/secondary/entry/north) "aMC" = ( /obj/structure/table, -/obj/machinery/computer/mob_healer_terminal, /turf/simulated/floor/carpet/arcade, /area/station/public/arcade) "aMD" = ( @@ -14876,9 +14875,6 @@ /area/station/medical/reception) "aVy" = ( /obj/machinery/economy/vending/coffee, -/obj/machinery/computer/mob_healer_terminal{ - pixel_x = 28 - }, /obj/structure/sign/poster/official/random{ pixel_y = 32 }, @@ -55778,13 +55774,6 @@ icon_state = "darkgreen" }, /area/station/medical/medbay3) -"fes" = ( -/obj/structure/chair{ - dir = 4 - }, -/obj/machinery/computer/mob_battle_terminal/red, -/turf/simulated/floor/plasteel, -/area/station/public/dorms) "feN" = ( /turf/simulated/floor/plasteel, /area/station/hallway/secondary/entry/north) @@ -65342,12 +65331,6 @@ /obj/effect/spawner/window/reinforced, /turf/simulated/floor/plating, /area/station/supply/lobby) -"jPN" = ( -/obj/machinery/computer/mob_battle_terminal/blue{ - pixel_x = -32 - }, -/turf/simulated/floor/plasteel, -/area/station/public/dorms) "jPZ" = ( /obj/structure/sign/poster/official/cleanliness{ pixel_x = 32 @@ -76992,10 +76975,6 @@ }, /turf/simulated/floor/plating, /area/station/security/permabrig) -"pwU" = ( -/obj/effect/landmark/battle_mob_point, -/turf/simulated/floor/engine, -/area/holodeck/alphadeck) "pxv" = ( /obj/machinery/atmospherics/unary/outlet_injector/on{ dir = 8; @@ -132341,7 +132320,7 @@ nXr aKQ fRL aPm -jPN +aPm lkw fho aPm @@ -132590,7 +132569,7 @@ awE ayf azn arR -fes +tbC tbC tbC aPm @@ -133621,10 +133600,10 @@ atG sHt sHt sHt -pwU sHt sHt -pwU +sHt +sHt sHt sHt sHt diff --git a/_maps/map_files/stations/cerestation.dmm b/_maps/map_files/stations/cerestation.dmm index 817cd6ed17c9..0eba8dd99f61 100644 --- a/_maps/map_files/stations/cerestation.dmm +++ b/_maps/map_files/stations/cerestation.dmm @@ -82602,12 +82602,6 @@ icon_state = "whitegreen" }, /area/station/medical/virology) -"tpw" = ( -/obj/effect/landmark/battle_mob_point, -/turf/simulated/floor/engine{ - name = "Holodeck Projector Floor" - }, -/area/holodeck/alphadeck) "tpA" = ( /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -116641,10 +116635,10 @@ bEM pDn pDn pDn -tpw pDn pDn -tpw +pDn +pDn pDn pDn pDn diff --git a/_maps/map_files/stations/metastation.dmm b/_maps/map_files/stations/metastation.dmm index ca66c423b305..82aac31c65a6 100644 --- a/_maps/map_files/stations/metastation.dmm +++ b/_maps/map_files/stations/metastation.dmm @@ -806,9 +806,6 @@ /turf/simulated/wall, /area/station/public/arcade) "ajb" = ( -/obj/machinery/computer/mob_battle_terminal/red{ - pixel_y = 30 - }, /obj/item/kirbyplants/plant20, /turf/simulated/floor/plasteel{ icon_state = "darkgrey" @@ -3389,9 +3386,6 @@ name = "east bump"; pixel_x = 27 }, -/obj/machinery/computer/mob_battle_terminal/blue{ - pixel_y = -30 - }, /turf/simulated/floor/plasteel{ icon_state = "darkgrey" }, @@ -53400,9 +53394,6 @@ /obj/structure/table, /obj/item/stack/medical/bruise_pack, /obj/item/stack/medical/ointment, -/obj/machinery/computer/mob_healer_terminal{ - pixel_y = 30 - }, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "whiteblue" diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 1d4d33ac2644..ea134741bec4 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -717,9 +717,12 @@ // /obj/item/gun signals -/// called in /obj/item/gun/process_fire (user, target, params, zone_override) -#define COMSIG_MOB_FIRED_GUN "mob_fired_gun" -/// called in /obj/item/gun/process_fire (user, target) +///called in /obj/item/gun/fire_gun (user, target, flag, params) +#define COMSIG_GUN_TRY_FIRE "gun_try_fire" + #define COMPONENT_CANCEL_GUN_FIRE (1<<0) +///called in /obj/item/gun/afterattack (user, target, flag, params) +#define COMSIG_MOB_TRY_FIRE "mob_fired_gun" +///called in /obj/item/gun/process_fire (user, target) #define COMSIG_GUN_FIRED "gun_fired" /// called in /datum/component/automatic_fire/proc/on_mouse_down: (client/clicker, atom/target, turf/location, control, params) #define COMSIG_AUTOFIRE_ONMOUSEDOWN "autofire_onmousedown" diff --git a/code/__DEFINES/mob_defines.dm b/code/__DEFINES/mob_defines.dm index 5d57157cfb43..5c1d026c3aec 100644 --- a/code/__DEFINES/mob_defines.dm +++ b/code/__DEFINES/mob_defines.dm @@ -298,6 +298,7 @@ #define HEALTH_HUD_OVERRIDE_HEALTHY 3 // Eye protection #define FLASH_PROTECTION_VERYVUNERABLE -4 +#define FLASH_PROTECTION_EXTRA_SENSITIVE -2 #define FLASH_PROTECTION_SENSITIVE -1 #define FLASH_PROTECTION_NONE 0 #define FLASH_PROTECTION_FLASH 1 diff --git a/code/__DEFINES/nanomob_defines.dm b/code/__DEFINES/nanomob_defines.dm deleted file mode 100644 index 0e3510e62c3e..000000000000 --- a/code/__DEFINES/nanomob_defines.dm +++ /dev/null @@ -1,17 +0,0 @@ -#define NANOMOB_TYPE_FIRE /datum/mob_type/fire -#define NANOMOB_TYPE_WATER /datum/mob_type/water -#define NANOMOB_TYPE_GRASS /datum/mob_type/grass -#define NANOMOB_TYPE_ELECTRIC /datum/mob_type/electric -#define NANOMOB_TYPE_GROUND /datum/mob_type/ground -#define NANOMOB_TYPE_ROCK /datum/mob_type/rock -#define NANOMOB_TYPE_BUG /datum/mob_type/bug -#define NANOMOB_TYPE_POISON /datum/mob_type/poison -#define NANOMOB_TYPE_NORMAL /datum/mob_type/normal -#define NANOMOB_TYPE_FIGHTING /datum/mob_type/fighting -#define NANOMOB_TYPE_PSYCHIC /datum/mob_type/psychic -#define NANOMOB_TYPE_GHOST /datum/mob_type/ghost -#define NANOMOB_TYPE_ICE /datum/mob_type/ice -#define NANOMOB_TYPE_FLYING /datum/mob_type/flying -#define NANOMOB_TYPE_BLUESPACE /datum/mob_type/bluespace -#define NANOMOB_TYPE_DARK /datum/mob_type/dark -#define NANOMOB_TYPE_STEEL /datum/mob_type/steel diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 335ac141fc8f..b213db5ac2dc 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -91,7 +91,6 @@ // If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child) #define FIRE_PRIORITY_PING 10 -#define FIRE_PRIORITY_NANOMOB 10 #define FIRE_PRIORITY_NIGHTSHIFT 10 #define FIRE_PRIORITY_IDLE_NPC 10 #define FIRE_PRIORITY_CLEANUP 10 diff --git a/code/__DEFINES/zoom.dm b/code/__DEFINES/zoom.dm new file mode 100644 index 000000000000..f36a97019aa2 --- /dev/null +++ b/code/__DEFINES/zoom.dm @@ -0,0 +1,5 @@ +///How the scope component is toggled. +/// Wielding the object with both hands toggles the zoom. Requires the two-handed component to work. +#define ZOOM_METHOD_WIELD 1 +/// Activated by clicking an item action button specified by the `item_action_type` var. +#define ZOOM_METHOD_ITEM_ACTION 2 diff --git a/code/__HELPERS/iconprocs.dm b/code/__HELPERS/iconprocs.dm index 533d66d52d30..e3e948f64f1f 100644 --- a/code/__HELPERS/iconprocs.dm +++ b/code/__HELPERS/iconprocs.dm @@ -445,9 +445,9 @@ GLOBAL_LIST_EMPTY(bicon_cache) * * frame - what frame of the icon_state's animation for the icon being used * * moving - whether or not to use a moving state for the given icon * * sourceonly - if TRUE, only generate the asset and send back the asset url, instead of tags that display the icon to players - * * extra_clases - string of extra css classes to use when returning the icon string + * * extra_classes - string of extra css classes to use when returning the icon string */ -/proc/icon2html(atom/thing, client/target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE, extra_classes = null) +/proc/icon2asset(atom/thing, client/target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE, extra_classes = null) if(!thing) return @@ -516,17 +516,15 @@ GLOBAL_LIST_EMPTY(bicon_cache) SSassets.transport.register_asset(key, rsc_ref, file_hash, icon_path) for(var/client_target in targets) SSassets.transport.send_assets(client_target, key) - if(sourceonly) - return SSassets.transport.get_asset_url(key) - return "" + return key -/// Costlier version of icon2html() that uses getFlatIcon() to account for overlays, underlays, etc. Use with extreme moderation, ESPECIALLY on mobs. -/proc/costly_icon2html(thing, target, sourceonly = FALSE) +/// Costlier version of icon2asset() that uses getFlatIcon() to account for overlays, underlays, etc. Use with extreme moderation, ESPECIALLY on mobs. +/proc/costly_icon2asset(thing, target, sourceonly = FALSE) if(!thing) return if(isicon(thing)) - return icon2html(thing, target) + return icon2asset(thing, target) var/icon/I = getFlatIcon(thing) - return icon2html(I, target, sourceonly = sourceonly) + return icon2asset(I, target) diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm index aeb341bc7ea8..3cb5928179a1 100644 --- a/code/__HELPERS/trait_helpers.dm +++ b/code/__HELPERS/trait_helpers.dm @@ -223,6 +223,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_CAN_VIEW_HEALTH "can_view_health" // Also used for /Stat #define TRAIT_MAGPULSE "magnetificent" // Used for anything that is magboot related #define TRAIT_NOSLIP "noslip" +#define TRAIT_SCOPED "user_scoped" #define TRAIT_MEPHEDRONE_ADAPTED "mephedrone_adapted" // Trait that changes the ending effects of twitch leaving your system #define TRAIT_NOKNOCKDOWNSLOWDOWN "noknockdownslowdown" //If this person has this trait, they are not slowed via knockdown, but they can be hit by bullets like a self knockdown #define TRAIT_CAN_STRIP "can_strip" // This mob can strip other mobs. diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index 4bd180b82225..2e1fc13f5db9 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -88,6 +88,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_FORCED_STANDING" = TRAIT_FORCED_STANDING, "TRAIT_NOSLIP" = TRAIT_NOSLIP, "TRAIT_MAGPULSE" = TRAIT_MAGPULSE, + "TRAIT_SCOPED" = TRAIT_SCOPED, "TRAIT_MEPHEDRONE_ADAPTED" = TRAIT_MEPHEDRONE_ADAPTED, "TRAIT_NOKNOCKDOWNSLOWDOWN" = TRAIT_NOKNOCKDOWNSLOWDOWN, "TRAIT_CAN_STRIP" = TRAIT_CAN_STRIP diff --git a/code/controllers/subsystem/SSnano_mob_hunter.dm b/code/controllers/subsystem/SSnano_mob_hunter.dm deleted file mode 100644 index 93e622f3ae1b..000000000000 --- a/code/controllers/subsystem/SSnano_mob_hunter.dm +++ /dev/null @@ -1,155 +0,0 @@ -SUBSYSTEM_DEF(mob_hunt) - name = "Nano-Mob Hunter GO Server" - priority = FIRE_PRIORITY_NANOMOB // Low priority, no need for MC_TICK_CHECK due to extremely low performance impact. - flags = SS_NO_INIT - offline_implications = "Nano-Mob Hunter will no longer spawn mobs. No immediate action is needed." - cpu_display = SS_CPUDISPLAY_LOW - var/max_normal_spawns = 15 //change this to adjust the number of normal spawns that can exist at one time. trapped spawns (from traitors) don't count towards this - var/list/normal_spawns = list() - var/max_trap_spawns = 15 //change this to adjust the number of trap spawns that can exist at one time. traps spawned beyond this point clear the oldest traps - var/list/trap_spawns = list() - var/list/connected_clients = list() - var/server_status = 1 //1 is online, 0 is offline - var/reset_cooldown = 0 //number of controller cycles before the manual_reboot proc can be used again (ignored if server is offline so you can always boot back up) - var/obj/machinery/computer/mob_battle_terminal/red_terminal - var/obj/machinery/computer/mob_battle_terminal/blue_terminal - var/battle_turn = null - -/datum/controller/subsystem/mob_hunt/fire(resumed = FALSE) - if(reset_cooldown) //if reset_cooldown is set (we are on cooldown, duh), reduce the remaining cooldown every cycle - reset_cooldown-- - if(!server_status) - return - client_mob_update() - if(length(normal_spawns) < max_normal_spawns) - spawn_mob() - -//leaving this here in case admins want to use it for a random mini-event or something -/datum/controller/subsystem/mob_hunt/proc/server_crash(recover_time = 3000) - server_status = 0 - for(var/datum/data/pda/app/mob_hunter_game/client in connected_clients) - client.disconnect("Server Crash") - for(var/obj/effect/nanomob/N in trap_spawns) - N.despawn() - for(var/obj/effect/nanomob/N in normal_spawns) - N.despawn() - //just in case - normal_spawns.Cut() - trap_spawns.Cut() - connected_clients.Cut() - if(!isnum(recover_time)) - recover_time = 3000 - if(recover_time > 0) //when provided with a negative or zero valued recover_time argument, the server won't auto-restart but can be manually rebooted still - //set a timer to automatically recover after recover_time has passed (can be manually restarted if you get impatient too) - addtimer(CALLBACK(src, PROC_REF(auto_recover)), recover_time, TIMER_UNIQUE) - -/datum/controller/subsystem/mob_hunt/proc/client_mob_update() - var/list/ex_players = list() - for(var/datum/data/pda/app/mob_hunter_game/client in connected_clients) - var/mob/living/carbon/human/H = client.get_player() - if(connected_clients[client]) - if(!H || H != connected_clients[client]) - ex_players |= connected_clients[client] - connected_clients[client] = H - if(length(ex_players)) //to make sure we don't do this if we didn't lose any player since the last update - for(var/obj/effect/nanomob/N in (normal_spawns + trap_spawns)) - N.conceal(ex_players) - -/datum/controller/subsystem/mob_hunt/proc/auto_recover() - if(server_status != 0) - return - server_status = 1 - while(length(normal_spawns) < max_normal_spawns) //repopulate the server's spawns completely if we auto-recover from crash - spawn_mob() - -/datum/controller/subsystem/mob_hunt/proc/manual_reboot() - if(server_status && reset_cooldown) - return 0 - for(var/obj/effect/nanomob/N in trap_spawns) - N.despawn() - for(var/obj/effect/nanomob/N in normal_spawns) - N.despawn() - server_status = 1 - reset_cooldown = 25 //25 controller cycle cooldown for manual restarts - return 1 - -/datum/controller/subsystem/mob_hunt/proc/spawn_mob() - var/list/nanomob_types = subtypesof(/datum/mob_hunt) - var/datum/mob_hunt/mob_info = pick(nanomob_types) - new mob_info() - -/datum/controller/subsystem/mob_hunt/proc/register_spawn(datum/mob_hunt/mob_info) - if(!mob_info) - return 0 - var/obj/effect/nanomob/new_mob = new /obj/effect/nanomob(mob_info.spawn_point, mob_info) - normal_spawns += new_mob - new_mob.reveal() - return 1 - -/datum/controller/subsystem/mob_hunt/proc/register_trap(datum/mob_hunt/mob_info) - if(!mob_info) - return 0 - if(!mob_info.is_trap) - return register_spawn(mob_info) - var/obj/effect/nanomob/new_mob = new /obj/effect/nanomob(mob_info.spawn_point, mob_info) - trap_spawns += new_mob - new_mob.reveal() - if(length(trap_spawns) > max_trap_spawns) - var/obj/effect/nanomob/old_trap = trap_spawns[1] - old_trap.despawn() - return 1 - -/datum/controller/subsystem/mob_hunt/proc/start_check() - if(battle_turn) //somehow we got called mid-battle, so lets just stop now - return - if(red_terminal && red_terminal.ready && blue_terminal && blue_terminal.ready) - battle_turn = pick("Red", "Blue") - red_terminal.atom_say("Battle starting!") - blue_terminal.atom_say("Battle starting!") - if(battle_turn == "Red") - red_terminal.atom_say("Red Player's Turn!") - else if(battle_turn == "Blue") - blue_terminal.atom_say("Blue Player's Turn!") - -/datum/controller/subsystem/mob_hunt/proc/launch_attack(team, raw_damage, datum/mob_type/attack_type) - if(!team || !raw_damage) - return - var/obj/machinery/computer/mob_battle_terminal/target = null - if(team == "Red") - target = blue_terminal - else if(team == "Blue") - target = red_terminal - else - return - target.receive_attack(raw_damage, attack_type) - -/datum/controller/subsystem/mob_hunt/proc/end_battle(loser, surrender = 0) - var/obj/machinery/computer/mob_battle_terminal/winner_terminal = null - var/obj/machinery/computer/mob_battle_terminal/loser_terminal = null - if(loser == "Red") - loser_terminal = red_terminal - winner_terminal = blue_terminal - else if(loser == "Blue") - loser_terminal = blue_terminal - winner_terminal = red_terminal - battle_turn = null - winner_terminal.ready = FALSE - loser_terminal.ready = FALSE - if(surrender) //surrender doesn't give exp, to avoid people just farming exp without actually doing a battle - winner_terminal.atom_say("Your rival surrendered!") - else - var/progress_message = winner_terminal.mob_info.gain_exp() - winner_terminal.atom_say("[winner_terminal.team] Player wins!") - winner_terminal.atom_say(progress_message) - -/datum/controller/subsystem/mob_hunt/proc/end_turn() - red_terminal.updateUsrDialog() - blue_terminal.updateUsrDialog() - if(!battle_turn) - return - if(battle_turn == "Red") - battle_turn = "Blue" - blue_terminal.atom_say("Blue's turn.") - else if(battle_turn == "Blue") - battle_turn = "Red" - blue_terminal.atom_say("Red's turn.") diff --git a/code/controllers/subsystem/SSstatpanel.dm b/code/controllers/subsystem/SSstatpanel.dm index e584162e5660..08840da95412 100644 --- a/code/controllers/subsystem/SSstatpanel.dm +++ b/code/controllers/subsystem/SSstatpanel.dm @@ -112,11 +112,10 @@ SUBSYSTEM_DEF(statpanels) /// Set the atoms we're meant to display var/datum/object_window_info/obj_window = istype(target.obj_window) ? target.obj_window : new(target) obj_window.atoms_to_show = atoms_to_display - START_PROCESSING(SSobj_tab_items, obj_window) - refresh_client_obj_view(target) + refresh_client_obj_view(target, obj_window.min_index, obj_window.max_index) -/datum/controller/subsystem/statpanels/proc/refresh_client_obj_view(client/refresh) - var/list/turf_items = return_object_images(refresh) +/datum/controller/subsystem/statpanels/proc/refresh_client_obj_view(client/refresh, min_index = 0, max_index = 30) + var/list/turf_items = return_object_images(refresh, min_index, max_index) if(!length(turf_items) || !refresh.mob?.listed_turf) return refresh.stat_panel.send_message("update_listedturf", turf_items) @@ -125,7 +124,7 @@ SUBSYSTEM_DEF(statpanels) /// Returns all our ready object tab images /// Returns a list in the form list(list(object_name, object_ref, loaded_image), ...) -/datum/controller/subsystem/statpanels/proc/return_object_images(client/load_from) +/datum/controller/subsystem/statpanels/proc/return_object_images(client/load_from, min_index, max_index) // You might be inclined to think that this is a waste of cpu time, since we // A: Double iterate over atoms in the build case, or // B: Generate these lists over and over in the refresh case @@ -137,10 +136,18 @@ SUBSYSTEM_DEF(statpanels) return list() var/datum/object_window_info/obj_window = load_from.obj_window + if(!obj_window) + return list() var/list/already_seen = obj_window.atoms_to_images var/list/to_make = obj_window.atoms_to_imagify var/list/turf_items = list() + var/i = 0 for(var/atom/turf_item as anything in obj_window.atoms_to_show) + // Limit what we send to the client's rendered section. + i++ + if(i <= min_index || i > max_index) + continue + // First, we fill up the list of refs to display // If we already have one, just use that var/existing_image = already_seen[turf_item] @@ -148,12 +155,17 @@ SUBSYSTEM_DEF(statpanels) continue // We already have it. Success! if(existing_image) - turf_items[++turf_items.len] = list("[turf_item.name]", turf_item.UID(), existing_image) + turf_items["[i]"] = list("[turf_item.name]", turf_item.UID(), SSassets.transport.get_asset_url(existing_image), existing_image) continue // Now, we're gonna queue image generation out of those refs to_make += turf_item already_seen[turf_item] = OBJ_IMAGE_LOADING obj_window.RegisterSignal(turf_item, COMSIG_PARENT_QDELETING, TYPE_PROC_REF(/datum/object_window_info, viewing_atom_deleted), override = TRUE) // we reset cache if anything in it gets deleted + turf_items["total"] = i + obj_window.min_index = min_index + obj_window.max_index = max_index + if(length(to_make)) + START_PROCESSING(SSobj_tab_items, obj_window) return turf_items #undef OBJ_IMAGE_LOADING @@ -219,6 +231,10 @@ SUBSYSTEM_DEF(statpanels) var/client/parent /// Are we currently tracking a turf? var/actively_tracking = FALSE + /// The minimum index currently sent to the client. + var/min_index = 0 + /// The maximum index currently sent to the client. + var/max_index = 30 /datum/object_window_info/New(client/parent) . = ..() @@ -245,9 +261,9 @@ SUBSYSTEM_DEF(statpanels) var/generated_string if(ismob(thing) || length(thing.overlays) > 2) - generated_string = costly_icon2html(thing, parent, sourceonly=TRUE) + generated_string = costly_icon2asset(thing, parent) else - generated_string = icon2html(thing, parent, sourceonly=TRUE) + generated_string = icon2asset(thing, parent) newly_seen[thing] = generated_string if(TICK_CHECK) @@ -257,7 +273,7 @@ SUBSYSTEM_DEF(statpanels) // If we've not cut yet, do it now if(index) to_make.Cut(1, index + 1) - SSstatpanels.refresh_client_obj_view(parent) + SSstatpanels.refresh_client_obj_view(parent, min_index, max_index) if(!length(to_make)) return PROCESS_KILL @@ -302,6 +318,9 @@ SUBSYSTEM_DEF(statpanels) if(listed_turf) client.stat_panel.send_message("create_listedturf", listed_turf.name) client.obj_window.start_turf_tracking() + client.obj_window.min_index = 0 + client.obj_window.max_index = 30 + SSstatpanels.set_turf_examine_tab(client, src) else client.stat_panel.send_message("remove_listedturf") client.obj_window.stop_turf_tracking() diff --git a/code/datums/action.dm b/code/datums/action.dm index eb6a1a19005f..0a099de316c6 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -78,6 +78,8 @@ /datum/action/proc/Trigger(left_click = TRUE) if(!IsAvailable()) return FALSE + if(SEND_SIGNAL(src, COMSIG_ACTION_TRIGGER, src) & COMPONENT_ACTION_BLOCK_TRIGGER) + return FALSE return TRUE /datum/action/proc/AltTrigger() @@ -570,7 +572,6 @@ var/obj/item/clothing/shoes/magboots/gravity/G = target G.dash(usr) - ///prset for organ actions /datum/action/item_action/organ_action check_flags = AB_CHECK_CONSCIOUS diff --git a/code/datums/components/scope.dm b/code/datums/components/scope.dm new file mode 100644 index 000000000000..0763330d7525 --- /dev/null +++ b/code/datums/components/scope.dm @@ -0,0 +1,273 @@ +///A component that allows players to use the item to zoom out. Mainly intended for firearms, but now works with other items too. +/datum/component/scope + /// How far the view can be moved from the player. At 1, it can be moved by the player's view distance; other values scale linearly. + var/range_modifier = 1 + /// Fullscreen object we use for tracking. + var/atom/movable/screen/fullscreen/stretch/cursor_catcher/scope/tracker + /// The owner of the tracker's ckey. For comparing with the current owner mob, in case the client has left it (e.g. ghosted). + var/tracker_owner_ckey + /// The method which we zoom in and out + var/zoom_method = ZOOM_METHOD_ITEM_ACTION + /// if not null, an item action will be added. Redundant if the mode is ZOOM_METHOD_RIGHT_CLICK or ZOOM_METHOD_WIELD. + var/item_action_type + /// Time to scope up, if you want the scope to take time to boot up. Used by the LWAP + var/time_to_scope + /// Do we let the user scope and click on the middle of their screen? + var/allow_middle_click = FALSE + /// Do we have the scope cancel on move? + var/movement_cancels_scope = FALSE + +/datum/component/scope/Initialize(range_modifier = 1, zoom_method = ZOOM_METHOD_ITEM_ACTION, item_action_type = /datum/action/zoom, time_to_scope = 0, allow_middle_click = FALSE, movement_cancels_scope = FALSE) + if(!isitem(parent)) + return COMPONENT_INCOMPATIBLE + src.range_modifier = range_modifier + src.zoom_method = zoom_method + src.item_action_type = item_action_type + src.time_to_scope = time_to_scope + src.allow_middle_click = allow_middle_click + src.movement_cancels_scope = movement_cancels_scope + + +/datum/component/scope/Destroy(force) + if(is_zoomed_in()) + stop_zooming(tracker.owner) + return ..() + +/datum/component/scope/RegisterWithParent() + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) //Checks for being removed for person, not mob movement + if(zoom_method == ZOOM_METHOD_WIELD) + RegisterSignal(parent, SIGNAL_ADDTRAIT(TRAIT_WIELDED), PROC_REF(on_wielded)) + RegisterSignal(parent, SIGNAL_REMOVETRAIT(TRAIT_WIELDED), PROC_REF(on_unwielded)) + if(item_action_type) + var/obj/item/parent_item = parent + var/datum/action/scope = new item_action_type(parent) + parent_item.actions += scope + RegisterSignal(scope, COMSIG_ACTION_TRIGGER, PROC_REF(on_action_trigger)) + RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine)) + if(istype(parent, /obj/item/gun)) + RegisterSignal(parent, COMSIG_GUN_TRY_FIRE, PROC_REF(on_gun_fire)) + +/datum/component/scope/UnregisterFromParent() + if(item_action_type) + var/obj/item/parent_item = parent + var/datum/action/scope = locate(item_action_type) in parent_item.actions + parent_item.actions -= scope + UnregisterSignal(parent, list( + COMSIG_MOVABLE_MOVED, + SIGNAL_ADDTRAIT(TRAIT_WIELDED), + SIGNAL_REMOVETRAIT(TRAIT_WIELDED), + COMSIG_GUN_TRY_FIRE, + COMSIG_PARENT_EXAMINE, + )) + +/datum/component/scope/process() + var/mob/user_mob = tracker.owner + var/client/user_client = user_mob.client + if(!user_client) + stop_zooming(user_mob) + return + tracker.calculate_params() + animate(user_client, world.tick_lag, pixel_x = tracker.given_x, pixel_y = tracker.given_y) + +/datum/component/scope/proc/on_move(atom/movable/source, atom/oldloc, dir, forced) + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED + + if(!is_zoomed_in()) + return + if(source.loc != tracker.owner) //Dropped. + to_chat(tracker.owner, "[parent]'s scope is overloaded by movement and shuts down!") + stop_zooming(tracker.owner) + +/datum/component/scope/proc/on_action_trigger(datum/action/source) + SIGNAL_HANDLER // COMSIG_ACTION_TRIGGER + var/obj/item/item = source.target + var/mob/living/user = item.loc + if(is_internal_organ(item)) + var/obj/item/organ/internal/O = item + user = O.owner + if(is_zoomed_in()) + stop_zooming(user) + else + INVOKE_ASYNC(src, PROC_REF(zoom), user) + +/datum/component/scope/proc/on_wielded(obj/item/source, trait) + SIGNAL_HANDLER // SIGNAL_ADDTRAIT(TRAIT_WIELDED) + var/mob/living/user = source.loc + INVOKE_ASYNC(src, PROC_REF(zoom), user) + +/datum/component/scope/proc/on_unwielded(obj/item/source, trait) + SIGNAL_HANDLER // SIGNAL_REMOVETRAIT(TRAIT_WIELDED) + var/mob/living/user = source.loc + stop_zooming(user) + +/datum/component/scope/proc/on_gun_fire(obj/item/gun/source, mob/living/user, atom/target, flag, params) + SIGNAL_HANDLER // COMSIG_GUN_TRY_FIRE + if(!tracker?.given_turf || target == get_target(tracker.given_turf)) + return NONE + INVOKE_ASYNC(source, TYPE_PROC_REF(/obj/item, afterattack), get_target(tracker.given_turf), user) + return COMPONENT_CANCEL_GUN_FIRE + +/datum/component/scope/proc/on_examine(datum/source, mob/user, list/examine_list) + SIGNAL_HANDLER // COMSIG_PARENT_EXAMINE + + var/scope = istype(parent, /obj/item/gun) ? "scope in" : "zoom out" + switch(zoom_method) + if(ZOOM_METHOD_WIELD) + examine_list += "You can [scope] by wielding it with both hands." + +/** + * We find and return the best target to hit on a given turf. + * + * Arguments: + * * target_turf: The turf we are looking for targets on. +*/ +/datum/component/scope/proc/get_target(turf/target_turf) + var/list/non_dense_targets = list() + for(var/atom/movable/possible_target in target_turf) + if(possible_target.layer <= PROJECTILE_HIT_THRESHHOLD_LAYER) + continue + if(possible_target.invisibility > tracker.owner.see_invisible) + continue + if(!possible_target.mouse_opacity) + continue + if(iseffect(possible_target)) + continue + if(ismob(possible_target)) + if(possible_target == tracker.owner) + continue + return possible_target + if(!possible_target.density) + non_dense_targets += possible_target + continue + return possible_target + if(length(non_dense_targets)) + return non_dense_targets[1] + return target_turf + +/** + * We start zooming by adding our tracker overlay and starting our processing. + * + * Arguments: + * * user: The mob we are starting zooming on. +*/ +/datum/component/scope/proc/zoom(mob/user) + if(isnull(user.client)) + return + if(HAS_TRAIT(user, TRAIT_SCOPED)) + to_chat(user, "You are already zoomed in!") + return + if(time_to_scope) + if(!do_after_once(user, time_to_scope, target = parent)) + return + user.playsound_local(parent, 'sound/weapons/scope.ogg', 75, TRUE) + tracker = user.overlay_fullscreen("scope", /atom/movable/screen/fullscreen/stretch/cursor_catcher/scope, istype(parent, /obj/item/gun)) + tracker.assign_to_mob(user, range_modifier) + if(movement_cancels_scope) + RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) + if(allow_middle_click) + RegisterSignal(tracker, COMSIG_CLICK, PROC_REF(generic_click)) + tracker_owner_ckey = user.ckey + if(user.is_holding(parent)) + RegisterSignals(user, list(COMSIG_CARBON_SWAP_HANDS, COMSIG_PARENT_QDELETING), PROC_REF(stop_zooming)) + else // The item is likely worn. + RegisterSignal(user, COMSIG_PARENT_QDELETING, PROC_REF(stop_zooming)) + var/static/list/capacity_signals = list( + COMSIG_LIVING_STATUS_PARALYSE, + COMSIG_LIVING_STATUS_STUN, + ) + RegisterSignals(user, capacity_signals, PROC_REF(on_incapacitated)) + START_PROCESSING(SSprojectiles, src) + ADD_TRAIT(user, TRAIT_SCOPED, "[UID(src)]") + if(istype(parent, /obj/item/gun)) + var/obj/item/gun/G = parent + G.on_scope_success(user) + return TRUE + +/datum/component/scope/proc/on_incapacitated(mob/living/source, amount = 0, ignore_canstun = FALSE) + SIGNAL_HANDLER // COMSIG_LIVING_STATUS_PARALYSE, COMSIG_LIVING_STATUS_STUN + + if(amount > 0) + stop_zooming(source) + +/datum/component/scope/proc/generic_click(/obj/source, location, control, params) + SIGNAL_HANDLER // COMSIG_CLICK + INVOKE_ASYNC(tracker.owner, TYPE_PROC_REF(/mob, ClickOn), get_target(tracker.given_turf), params) + +/** + * We stop zooming, canceling processing, resetting stuff back to normal and deleting our tracker. + * + * Arguments: + * * user: The mob we are canceling zooming on. +*/ +/datum/component/scope/proc/stop_zooming(mob/user) + SIGNAL_HANDLER // COMSIG_CARBON_SWAP_HANDS, COMSIG_PARENT_QDELETING + + if(!HAS_TRAIT(user, TRAIT_SCOPED)) + return + + STOP_PROCESSING(SSprojectiles, src) + UnregisterSignal(user, list( + COMSIG_LIVING_STATUS_PARALYSE, + COMSIG_LIVING_STATUS_STUN, + COMSIG_CARBON_SWAP_HANDS, + COMSIG_PARENT_QDELETING, + )) + REMOVE_TRAIT(user, TRAIT_SCOPED, "[UID(src)]") + + user.playsound_local(parent, 'sound/weapons/scope.ogg', 75, TRUE, frequency = -1) + user.clear_fullscreen("scope") + + // if the client has ended up in another mob, find that mob so we can fix their cursor + var/mob/true_user + if(user.ckey != tracker_owner_ckey) + true_user = get_mob_by_ckey(tracker_owner_ckey) + + if(!isnull(true_user)) + user = true_user + + if(user.client) + animate(user.client, 0.2 SECONDS, pixel_x = 0, pixel_y = 0) + UnregisterSignal(user, COMSIG_MOVABLE_MOVED) + QDEL_NULL(tracker) + tracker_owner_ckey = null + if(istype(parent, /obj/item/gun)) + var/obj/item/gun/G = parent + G.on_scope_end(user) + +/datum/component/scope/proc/is_zoomed_in() + return !!tracker + +/atom/movable/screen/fullscreen/stretch/cursor_catcher/scope + icon = 'icons/mob/screen_scope.dmi' + icon_state = "scope" + /// Multiplier for given_X an given_y. + var/range_modifier = 1 + +/atom/movable/screen/fullscreen/stretch/cursor_catcher/scope/assign_to_mob(mob/new_owner, range_modifier) + src.range_modifier = range_modifier + return ..() + +/atom/movable/screen/fullscreen/stretch/cursor_catcher/scope/Click(location, control, params) + if(usr == owner) + calculate_params() + SEND_SIGNAL(src, COMSIG_CLICK, location, control, params) + + return ..() + +/atom/movable/screen/fullscreen/stretch/cursor_catcher/scope/calculate_params() + var/list/modifiers = params2list(mouse_params) + var/icon_x = text2num(LAZYACCESS(modifiers, "vis-x")) + if(isnull(icon_x)) + icon_x = text2num(LAZYACCESS(modifiers, "icon-x")) + var/icon_y = text2num(LAZYACCESS(modifiers, "vis-y")) + if(isnull(icon_y)) + icon_y = text2num(LAZYACCESS(modifiers, "icon-y")) + given_x = round(range_modifier * (icon_x - view_list[1] * world.icon_size / 2)) + given_y = round(range_modifier * (icon_y - view_list[2] * world.icon_size / 2)) + given_turf = locate(owner.x + round(given_x / world.icon_size, 1), owner.y + round(given_y / world.icon_size, 1), owner.z) + + +/datum/action/zoom + name = "Toggle Scope" + check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING + button_icon_state = "sniper_zoom" diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm index 1a0339564de5..275aa7bda3c0 100644 --- a/code/datums/status_effects/neutral.dm +++ b/code/datums/status_effects/neutral.dm @@ -218,26 +218,11 @@ tick_interval = 4 /// The number of people the gun has locked on to. Caps at 10 for sanity. var/locks = 0 - /// What direction the owner was in when using the scope. - var/owner_dir = 0 - -/datum/status_effect/lwap_scope/on_creation(mob/living/new_owner, stored_dir = 0) - owner_dir = stored_dir - return ..() /datum/status_effect/lwap_scope/tick() locks = 0 - var/turf/owner_turf = get_turf(owner) - var/scope_turf - for(var/turf/T in RANGE_EDGE_TURFS(7, owner_turf)) - if(get_dir(owner, T) != owner_dir) - continue - if(T in range(owner, 6)) - continue - scope_turf = T - break - if(scope_turf) - for(var/mob/living/L in range(10, scope_turf)) + for(var/atom/movable/screen/fullscreen/stretch/cursor_catcher/scope/our_scope in owner.client.screen) + for(var/mob/living/L in range(10, our_scope.given_turf)) if(locks >= LWAP_LOCK_CAP) return if(L == owner || L.stat == DEAD || isslime(L) || ismonkeybasic(L)) //xenobio moment diff --git a/code/datums/uplink_items/uplink_general.dm b/code/datums/uplink_items/uplink_general.dm index 9f8b899c38c5..d470f30593d5 100644 --- a/code/datums/uplink_items/uplink_general.dm +++ b/code/datums/uplink_items/uplink_general.dm @@ -853,6 +853,16 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/autosurgeon/organ/syndicate/razorwire cost = 20 +/datum/uplink_item/cyber_implants/scope_eyes + name = "Hardened Kaleido Optics Eyes Autoimplanter" + desc = "These cybernetic eye implants will let you zoom in on far away objects. \ + Many users find it disorienting, and find it hard to interact with things near them when active. \ + This pair has been hardened for special operations personnel." + reference = "KOE" + item = /obj/item/autosurgeon/organ/syndicate/scope_eyes + cost = 20 + + // POINTLESS BADASSERY /datum/uplink_item/badass diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 4b2541251358..36a0a848a2f2 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -300,20 +300,7 @@ emagged = FALSE setMenuState(ui.user, COMM_SCREEN_MAIN) - if("RestartNanoMob") - if(SSmob_hunt) - if(SSmob_hunt.manual_reboot()) - var/loading_msg = pick("Respawning spawns", "Reticulating splines", "Flipping hat", - "Capturing all of them", "Fixing minor text issues", "Being the very best", - "Nerfing this", "Not communicating with playerbase", "Coding a ripoff in a 2D spaceman game") - to_chat(ui.user, "Restarting Nano-Mob Hunter GO! game server. [loading_msg]...") - else - to_chat(ui.user, "Nano-Mob Hunter GO! game server reboot failed due to recent restart. Please wait before re-attempting.") - else - to_chat(ui.user, "Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.") - // ADMIN CENTCOMM ONLY STUFF - if("send_to_cc_announcement_page") if(!ADMIN_CHECK(ui.user)) return diff --git a/code/game/machinery/vendors/generic_vendors.dm b/code/game/machinery/vendors/generic_vendors.dm index a25e252705fc..d817a93c1fa8 100644 --- a/code/game/machinery/vendors/generic_vendors.dm +++ b/code/game/machinery/vendors/generic_vendors.dm @@ -1301,7 +1301,6 @@ icon_panel = "wide_vendor" category = VENDOR_TYPE_SUPPLIES products = list(/obj/item/pda =10, - /obj/item/cartridge/mob_hunt_game = 25, /obj/item/cartridge/medical = 10, /obj/item/cartridge/chemistry = 10, /obj/item/cartridge/engineering = 10, @@ -1314,7 +1313,6 @@ /obj/item/cartridge/mime = 1) prices = list(/obj/item/pda = 300, - /obj/item/cartridge/mob_hunt_game = 50, /obj/item/cartridge/medical = 200, /obj/item/cartridge/chemistry = 150, /obj/item/cartridge/engineering = 100, diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index a87d29096548..374a2fb1114d 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -678,8 +678,5 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/newplayer_start) //Without this you sp var/turf/simulated/T = get_turf(src) T.burn_tile() -/obj/effect/landmark/battle_mob_point - name = "Nanomob Battle Avatar Spawn Point" - /obj/effect/landmark/free_golem_spawn name = "Free Golem Spawn Point" diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm index f549f5f8cd3b..3720150cd1df 100644 --- a/code/game/objects/explosion.dm +++ b/code/game/objects/explosion.dm @@ -1,5 +1,3 @@ -//TODO: Flash range does nothing currently - #define CREAK_DELAY 5 SECONDS //Time taken for the creak to play after explosion, if applicable. #define DEVASTATION_PROB 30 //The probability modifier for devistation, maths! #define HEAVY_IMPACT_PROB 5 //ditto @@ -74,6 +72,10 @@ var/turf/M_turf = get_turf(M) if(M_turf && M_turf.z == z0) var/dist = get_dist(M_turf, epicenter) + if(isliving(M) && dist <= flash_range) + var/mob/living/to_flash = M + var/is_very_close_to_the_explosion = flash_range > (dist * 2) + to_flash.flash_eyes(is_very_close_to_the_explosion * 2, is_very_close_to_the_explosion, is_very_close_to_the_explosion) // Gets past sunglasses var/baseshakeamount if(orig_max_distance - dist > 0) baseshakeamount = sqrt((orig_max_distance - dist) * 0.1) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 0b9abc3abf31..baf89390823d 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -169,7 +169,8 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list( /client/proc/debug_timers, /client/proc/force_verb_bypass, /client/proc/show_gc_queues, - /client/proc/debug_global_variables + /client/proc/debug_global_variables, + /client/proc/profile_code )) GLOBAL_LIST_INIT(admin_verbs_possess, list( /proc/possess, @@ -236,7 +237,8 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list( /client/proc/debug_global_variables, /client/proc/visualise_active_turfs, /client/proc/debug_timers, - /client/proc/timer_log + /client/proc/timer_log, + /client/proc/profile_code )) /client/proc/add_admin_verbs() @@ -955,6 +957,12 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list( src.stat_panel.send_message("create_debug") +/client/proc/profile_code() + set name = "Profile Code" + set category = "Debug" + + winset(usr, null, "command=.profile") + /client/proc/export_current_character() set name = "Export Character DMI/JSON" set category = "Admin" diff --git a/code/modules/arcade/mob_hunt/battle_computer.dm b/code/modules/arcade/mob_hunt/battle_computer.dm deleted file mode 100644 index 228686fa951c..000000000000 --- a/code/modules/arcade/mob_hunt/battle_computer.dm +++ /dev/null @@ -1,309 +0,0 @@ - -/obj/machinery/computer/mob_battle_terminal - name = "Nano-Mob Hunter GO! Battle Terminal" - desc = "Insert a mob card to partake in life-like Nano-Mob Battle Action!" - icon_state = "mob_battle_empty" - icon_screen = null - icon_keyboard = null - density = FALSE - anchored = TRUE - var/obj/item/nanomob_card/card - var/datum/mob_hunt/mob_info - var/obj/effect/landmark/battle_mob_point/avatar_point - var/obj/effect/nanomob/battle/avatar - var/ready = FALSE - var/team = "Grey" - -/obj/machinery/computer/mob_battle_terminal/red - pixel_y = 24 - dir = SOUTH - team = "Red" - -/obj/machinery/computer/mob_battle_terminal/blue - pixel_y = -24 - dir = NORTH - team = "Blue" - -/obj/machinery/computer/mob_battle_terminal/red/Initialize() - ..() - check_connection() - -/obj/machinery/computer/mob_battle_terminal/blue/Initialize() - ..() - check_connection() - -/obj/machinery/computer/mob_battle_terminal/update_icon_state() - if(card) - icon_state = "mob_battle_loaded" - else - icon_state = "mob_battle_empty" - -/obj/machinery/computer/mob_battle_terminal/Destroy() - eject_card(1) - if(SSmob_hunt) - if(SSmob_hunt.battle_turn) - SSmob_hunt.battle_turn = null - if(SSmob_hunt.red_terminal == src) - SSmob_hunt.red_terminal = null - if(SSmob_hunt.blue_terminal == src) - SSmob_hunt.blue_terminal = null - QDEL_NULL(avatar) - return ..() - -/obj/machinery/computer/mob_battle_terminal/attackby(obj/item/O, mob/user) - if(istype(O, /obj/item/nanomob_card)) - insert_card(O, user) - return - return ..() - -/obj/machinery/computer/mob_battle_terminal/proc/insert_card(obj/item/nanomob_card/new_card, mob/user) - if(!new_card) - return - if(card) - to_chat(user, "The card slot is currently filled.") - return - if(!new_card.mob_data) - to_chat(user, "This is a blank mob card.") - return - if(new_card.mob_data && !new_card.mob_data.cur_health) - to_chat(user, "This mob is incapacitated! Heal it before attempting to use it in battle!") - return - user.unEquip(new_card) - new_card.forceMove(src) - card = new_card - mob_info = card.mob_data - update_icon() - update_avatar() - updateUsrDialog() - -/obj/machinery/computer/mob_battle_terminal/proc/eject_card(override = 0) - if(!override) - if(ready && SSmob_hunt.battle_turn != team) - atom_say("You can't recall on your rival's turn!") - return - if(!card) - return - card.mob_data = mob_info - mob_info = null - card.forceMove(get_turf(src)) - card = null - update_avatar() - update_icon() - updateUsrDialog() - -/obj/machinery/computer/mob_battle_terminal/proc/update_avatar() - //if we don't have avatars yet, spawn them - if(!avatar) - if(!avatar_point) - log_debug("[src] attempted to spawn a battle mob avatar without a spawn point!") - return - else - avatar = new(get_turf(avatar_point)) - //update avatar info from card - if(mob_info) - avatar.mob_info = mob_info - else - avatar.mob_info = null - //tell the avatar to update themself with the new info - avatar.update_self() - -/obj/machinery/computer/mob_battle_terminal/attack_hand(mob/user) - add_fingerprint(user) - interact(user) - -/obj/machinery/computer/mob_battle_terminal/attack_ai(mob/user) - to_chat(user, "You cannot interface with this portion of the simulation.") - return - -/obj/machinery/computer/mob_battle_terminal/interact(mob/user) - check_connection() - var/datum/asset/mob_hunt_asset = get_asset_datum(/datum/asset/simple/mob_hunt) - mob_hunt_asset.send(user) - - var/dat = "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - if(ready && SSmob_hunt.battle_turn) //offer the surrender option if they are in a battle (ready), but don't have a card loaded - dat += "" - dat += "" - dat += "" - else - dat += "
" - dat += "[team] PLAYER" - dat += "
" - if(!card) - dat += "

No Nano-Mob card loaded.

" - dat += "
Surrender!
" - dat += "" - dat += "" - if(mob_info.nickname) - dat += "" - dat += "" - if(mob_info.nickname) - dat += "" - dat += "" - dat += "" - dat += "
" - dat += "

[mob_info.mob_name]

" - dat += "
" - else - dat += "" - var/img_src = SSassets.transport.get_asset_url("[mob_info.icon_state_normal].png") - if(mob_info.is_shiny) - dat += SSassets.transport.get_asset_url("[mob_info.icon_state_shiny].png") - dat += "" - dat += "
" - dat += "

[mob_info.nickname]

" - dat += "
" - dat += "
" - dat += "Health: [mob_info.cur_health] / [mob_info.max_health]
" - dat += "" - dat += "" - if(mob_info.cur_health) - dat += "" - else - dat += "" - dat += "" - dat += "" - dat += "
Attack!Incapacitated!Recall!
" - dat += "" - dat += "" - if(!ready) - dat += "" - dat += "Battle!" - dat += "" - if(ready && !SSmob_hunt.battle_turn) - dat += "" - dat += "Cancel Battle!" - dat += "" - dat += "" - - var/datum/browser/popup = new(user, "mob_battle_terminal", "Nano-Mob Hunter GO! Battle Terminal", 575, 400) - popup.set_content(dat) - popup.open() - -/obj/machinery/computer/mob_battle_terminal/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["attack"]) - do_attack() - - if(href_list["eject"]) - eject_card() - - if(href_list["surrender"]) - surrender() - - if(href_list["ready"]) - var/option = text2num(href_list["ready"]) - if(option == 1) - start_battle() - else if(option == 2) - ready = FALSE - atom_say("[team] Player cancels their battle challenge.") - - updateUsrDialog() - -/obj/machinery/computer/mob_battle_terminal/proc/check_connection() - if(team == "Red") - if(SSmob_hunt && !SSmob_hunt.red_terminal) - SSmob_hunt.red_terminal = src - else if(team == "Blue") - if(SSmob_hunt && !SSmob_hunt.blue_terminal) - SSmob_hunt.blue_terminal = src - - find_avatar_spawn_point() - -/obj/machinery/computer/mob_battle_terminal/proc/find_avatar_spawn_point() - if(avatar_point) - return - var/obj/effect/landmark/battle_mob_point/closest - for(var/obj/effect/landmark/battle_mob_point/bmp in GLOB.landmarks_list) - if(!istype(bmp, /obj/effect/landmark/battle_mob_point)) - continue - if(bmp.z != z) //only match with points on the same z-level) - continue - if(!closest || isnull(closest)) - closest = bmp - continue - if(closest == bmp) - continue - if((abs(x-bmp.x)+abs(y-bmp.y)) < (abs(x-closest.x)+abs(y-closest.y))) //get_dist would be preferable if it didn't count diagonals as 1 distance, so we have to rely on actual x/y distances in this janky way. - closest = bmp - if(closest) - avatar_point = closest - else - log_debug("[src] was unable to locate a nearby mob battle landmark! Unable to spawn battle avatars!") - -/obj/machinery/computer/mob_battle_terminal/proc/do_attack() - if(!ready) //no attacking if you arent ready to fight (duh) - return - if(!SSmob_hunt || team != SSmob_hunt.battle_turn) //don't attack unless it is actually our turn - return - else - var/message = "[mob_info.mob_name] attacks!" - if(mob_info.nickname) - message = "[mob_info.nickname] attacks!" - atom_say(message) - SSmob_hunt.launch_attack(team, mob_info.get_raw_damage(), mob_info.get_attack_type()) - -/obj/machinery/computer/mob_battle_terminal/proc/start_battle() - if(ready) //don't do anything if we are still ready - return - if(!card) //don't do anything if there isn't a card inserted - return - ready = TRUE - atom_say("[team] Player is ready for battle! Waiting for rival...") - SSmob_hunt.start_check() - -/obj/machinery/computer/mob_battle_terminal/proc/receive_attack(raw_damage, datum/mob_type/attack_type) - var/message = mob_info.take_damage(raw_damage, attack_type) - avatar.audible_message(message, null, 5) - if(!mob_info.cur_health) - SSmob_hunt.end_battle(team) - eject_card(1) //force the card out, they were defeated - else - SSmob_hunt.end_turn() - -/obj/machinery/computer/mob_battle_terminal/proc/surrender() - atom_say("[team] Player surrenders the battle!") - SSmob_hunt.end_battle(team, 1) - -////////////////////////////// -// Mob Healing Terminal // -// (Pokemon Center) // -////////////////////////////// - -/obj/machinery/computer/mob_healer_terminal - name = "Nano-Mob Hunter GO! Restoration Terminal" - desc = "Swipe a mob card to instantly restore it to full health!" - icon_state = "mob_battle_loaded" - icon_screen = null - icon_keyboard = null - density = FALSE - anchored = TRUE - dir = EAST - -/obj/machinery/computer/mob_healer_terminal/attackby(obj/item/O, mob/user) - if(istype(O, /obj/item/nanomob_card)) - heal_card(O, user) - return - return ..() - -/obj/machinery/computer/mob_healer_terminal/proc/heal_card(obj/item/nanomob_card/patient, mob/user) - if(!patient) - return - if(!patient.mob_data) - to_chat(user, "This is a blank mob card.") - return - if(patient.mob_data && patient.mob_data.cur_health == patient.mob_data.max_health) - to_chat(user, "This mob is already at maximum health!") - return - patient.mob_data.cur_health = patient.mob_data.max_health - to_chat(user, "[patient.mob_data.nickname ? patient.mob_data.nickname : patient.mob_data.mob_name] has been restored to full health!") diff --git a/code/modules/arcade/mob_hunt/mob_avatar.dm b/code/modules/arcade/mob_hunt/mob_avatar.dm deleted file mode 100644 index ecab58523dbf..000000000000 --- a/code/modules/arcade/mob_hunt/mob_avatar.dm +++ /dev/null @@ -1,168 +0,0 @@ -/obj/effect/nanomob - name = "Nano-Mob Avatar" //will be overridden by the mob datum name value when created - desc = "A wild Nano-Mob appeared! Hit it with your PDA with the game open to attempt to capture it!" - invisibility = 101 - alpha = 128 - anchored = TRUE //just in case - density = FALSE - icon = 'icons/effects/mob_hunt.dmi' - var/state_name - var/datum/mob_hunt/mob_info = null - var/list/clients_encountered = list() //tracks who has already interacted with us, so they can't attempt a second capture - var/image/avatar - -/obj/effect/nanomob/Initialize(mapload, datum/mob_hunt/new_info) - . = ..() - if(!new_info) - return INITIALIZE_HINT_QDEL - mob_info = new_info - RegisterSignal(mob_info, COMSIG_PARENT_QDELETING, PROC_REF(delete_wrapper)) - update_self() - forceMove(mob_info.spawn_point) - if(!mob_info.is_trap) - addtimer(CALLBACK(src, PROC_REF(despawn)), mob_info.lifetime) - -/obj/effect/nanomob/Destroy() - SSmob_hunt.trap_spawns -= src - SSmob_hunt.normal_spawns -= src - mob_info = null // Can't delete this since multiple players can get the exact same /datum/mob_hunt. (This should be refactored) - clients_encountered.Cut() - QDEL_NULL(avatar) - return ..() - -/obj/effect/nanomob/proc/delete_wrapper() - SIGNAL_HANDLER - qdel(src) - -/obj/effect/nanomob/proc/update_self() - if(!mob_info) - return - name = mob_info.mob_name - desc = "A wild [name] (level [mob_info.level]) appeared! Hit it with your PDA with the game open to attempt to capture it!" - if(mob_info.is_shiny) - state_name = mob_info.icon_state_shiny - else - state_name = mob_info.icon_state_normal - avatar = image(icon, src, state_name) - avatar.override = 1 - add_alt_appearance("nanomob_avatar", avatar) - -/obj/effect/nanomob/attackby(obj/item/O, mob/user) - if(istype(O, /obj/item/pda)) - var/obj/item/pda/P = O - attempt_capture(P, -20) //attempting a melee capture reduces the mob's effective run_chance by 20% to balance the risk of triggering a trap mob - return 1 - -/obj/effect/nanomob/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - if(istype(AM, /obj/item/pda)) - var/obj/item/pda/P = AM - attempt_capture(P) //attempting a ranged capture does not affect the mob's effective run_chance but does prevent you from being shocked by a trap mob - return 1 - -/obj/effect/nanomob/proc/attempt_capture(obj/item/pda/P, catch_mod = 0) //negative catch_mods lower effective run chance, - if(!P || !P.current_app || !istype(P.current_app, /datum/data/pda/app/mob_hunter_game) || !P.cartridge) - return - - var/datum/data/pda/app/mob_hunter_game/client = P.current_app - var/total_catch_mod = client.catch_mod + catch_mod //negative values decrease the chance of the mob running, positive values makes it more likely to flee - if(!client.connected) //must be connected to attempt captures - P.atom_say("No server connection. Capture aborted.") - return - - if(mob_info.is_trap) //traps work even if you ran into them before, which is why this is before the clients_encountered check - if(client.hacked) //hacked copies of the game (copies capable of setting traps) are protected from traps - return - if(iscarbon(P.loc)) - var/mob/living/carbon/C = P.loc - //Strike them down with a lightning bolt to complete the illusion (copied from the surge reagent overdose, probably could make this a general-use proc in the future) - playsound(get_turf(C), 'sound/effects/eleczap.ogg', 75, 1) - var/icon/I=new('icons/obj/zap.dmi',"lightningend") - I.Turn(-135) - var/obj/effect/overlay/beam/B = new(get_turf(C)) - B.pixel_x = rand(-20, 0) - B.pixel_y = rand(-20, 0) - B.icon = I - //then actually do the damage/stun - C.electrocute_act(20, P, 1) //same damage as a revenant's overload ability, except subject to gloves/species shock resistance (for human mobs at least) - return - - if(client in clients_encountered) //we've already dealt with you, go away! - return - else //deal with the new hunter by either running away or getting caught - clients_encountered += client - var/message = null - var/effective_run_chance = mob_info.run_chance + total_catch_mod - if((effective_run_chance > 0) && prob(effective_run_chance)) - message += "Capture failed! [name] escaped [P.owner ? "from [P.owner]" : "from this hunter"]!" - conceal(client) - else - if(client.register_capture(mob_info, 1)) - message += "Capture success! [P.owner ? P.owner : "This hunter"] captured [name]!" - conceal(client) - else - message += "Capture error! Try again." - clients_encountered -= client //if the capture registration failed somehow, let them have another chance with this mob - P.atom_say(message) - -/obj/effect/nanomob/proc/despawn() - if(SSmob_hunt) - if(mob_info.is_trap) - SSmob_hunt.trap_spawns -= src - else - SSmob_hunt.normal_spawns -= src - qdel(src) - -/obj/effect/nanomob/proc/reveal() - if(!SSmob_hunt) - return - var/list/show_to = list() - for(var/A in SSmob_hunt.connected_clients) - if((A in clients_encountered) || !SSmob_hunt.connected_clients[A]) - continue - show_to |= SSmob_hunt.connected_clients[A] - display_alt_appearance("nanomob_avatar", show_to) - -/obj/effect/nanomob/proc/conceal(list/hide_from) - if(!SSmob_hunt) - return - var/list/hiding_from = list() - if(hide_from) - hiding_from = hide_from - else - for(var/A in SSmob_hunt.connected_clients) - if((A in clients_encountered) && SSmob_hunt.connected_clients[A]) - hiding_from |= SSmob_hunt.connected_clients[A] - hide_alt_appearance("nanomob_avatar", hiding_from) - -// BATTLE MOB AVATARS - -/obj/effect/nanomob/battle - name = "Nano-Mob Battle Avatar" - desc = "A new challenger approaches!" - invisibility = 0 - icon_state = "placeholder" - var/obj/machinery/computer/mob_battle_terminal/my_terminal - -/obj/effect/nanomob/battle/update_self() - if(!mob_info) - name = "Nano-Mob Battle Avatar" - desc = "A new challenger approaches" - icon_state = "placeholder" - else - name = mob_info.mob_name - desc = "A tamed [name] (level [mob_info.level]) ready for battle!" - if(mob_info.is_shiny) - icon_state = mob_info.icon_state_shiny - else - icon_state = mob_info.icon_state_normal - -/obj/effect/nanomob/battle/attempt_capture(obj/item/pda/P, catch_mod = 0) - //you can't capture battle avatars, since they belong to someone already - return - -//battle avatars are always visible, so we can ignore reveal and conceal calls for them -/obj/effect/nanomob/battle/reveal() - return - -/obj/effect/nanomob/battle/conceal() - return diff --git a/code/modules/arcade/mob_hunt/mob_cards.dm b/code/modules/arcade/mob_hunt/mob_cards.dm deleted file mode 100644 index 6acfe826f445..000000000000 --- a/code/modules/arcade/mob_hunt/mob_cards.dm +++ /dev/null @@ -1,43 +0,0 @@ - -//Standard Cards -/obj/item/nanomob_card - name = "Nano-Mob Hunter Trading Card" - desc = "A blank Nano-Mob Hunter Trading Card. Worthless!" - icon = 'icons/obj/card.dmi' - icon_state = "trade_card" - force = 0 - throwforce = 1 - w_class = WEIGHT_CLASS_TINY - var/datum/mob_hunt/mob_data - -/obj/item/nanomob_card/proc/update_info() - if(!mob_data) - return - if(mob_data.is_shiny) - name = "Holographic [mob_data.mob_name] Nano-Mob Hunter Card" - desc = "WOW! A holographic trading card containing a level [mob_data.level] [mob_data.mob_name]!" - icon_state = "trade_card_holo" - else - name = "[mob_data.mob_name] Nano-Mob Hunter Card" - desc = "A trading card containing a level [mob_data.level] [mob_data.mob_name]!" - -//Booster Pack Cards (random mob data) -/obj/item/nanomob_card/booster - name = "Nano-Mob Hunter Booster Pack Card" - desc = "A random Nano-Mob Trading Card from a Booster Pack. Wonder what it is?" - -/obj/item/nanomob_card/booster/New() - ..() - var/datum/mob_hunt/mob_info = pick(subtypesof(/datum/mob_hunt)) - mob_data = new mob_info(0,null,1) - update_info() - -//Booster Packs (Box of booster pack cards) -/obj/item/storage/box/nanomob_booster_pack - name = "Nano-Mob Hunter Trading Card Booster Pack" - desc = "Contains 6 random Nano-Mob Hunter Trading Cards. May contain a holographic card!" - can_hold = list(/obj/item/nanomob_card) - -/obj/item/storage/box/nanomob_booster_pack/populate_contents() - for(var/i in 1 to 6) - new /obj/item/nanomob_card/booster(src) diff --git a/code/modules/arcade/mob_hunt/mob_datums.dm b/code/modules/arcade/mob_hunt/mob_datums.dm deleted file mode 100644 index fc60e17bbbea..000000000000 --- a/code/modules/arcade/mob_hunt/mob_datums.dm +++ /dev/null @@ -1,480 +0,0 @@ - -/datum/mob_hunt - //GENERAL STATS AND VARIABLES - var/mob_name = "Generic Mob" //the mob's original name (its species/type/whatever) - var/nickname = "" //the mob's nickname (if given by the owner) - var/run_chance = 0 //percent chance the mob will escape capture attempts (higher is obviously more likely to get away) - - //COMBAT STATS AND VARIABLES - var/level = 0 //actual level of this mob (don't set this, it gets overwritten in New()) - var/min_level = 1 //minimum level of this mob (used for randomizing the actual level) - var/max_level = 1 //maximum level of this mob (used for randomizing the actual level) - var/exp = 0 //number of battles the mob has won towards the next level (resets to 0 on level-up) - var/exp_to_level = 3 //number of battles the mob must win to level up (in case we want to make some mobs harder or easier to level) - //the types of the mob will be used for battles to determine damage resistance or weakness (mob_type_datums.dm) - var/datum/mob_type/primary_type //Make sure you set this or the mob will be unable to deal damage and will take absurd damage in battles - var/datum/mob_type/secondary_type //Don't set if not a dual-type mob so the mob will only calculate damage based on primary type - var/base_attack = 5 //base damage dealt by the mob's attacks for battling (effectively damage dealt at level 0) - var/base_health = 5 //base health of the mob for battling (effectively max health at level 0) - var/attack_multiplier = 1 //how much additional damage per level the mob deals (level * attack_multiplier) - var/health_multiplier = 1 //how much additional health per level the mob gets (level * health_multiplier) for calculating max health - var/cur_health = 0 - var/max_health = 0 - - //SPAWN PREFERENCES AND VARIABLES - //A note on mob spawn preferences: The mob types also have preferences, which are handled prior to per-mob preferences, so ultimately you use a combined set of preferences - var/list/area_blacklist = list() //list of areas this mob can NOT spawn in (such as the bridge) - var/list/turf_blacklist = list() //list of turfs this mob can NOT spawn on (such as wood floors) - var/list/area_whitelist = list() //list of areas this mob is more likely to spawn in (can be used to reinclude subtypes of blacklisted areas) - var/list/turf_whitelist = list() //list of turfs this mob is more likely to spawn on (can be used to reinclude subtypes of blacklisted turfs) - var/turf/spawn_point //gets set and sent to the game server to spawn its avatar (generated in select_spawn or assigned via set_trap) - var/lifetime = 6000 //number of deciseconds the mob will remain before despawning (REMEMBER: DECISECONDS! So 6000 is 600 seconds which is 10 minutes) - var/is_shiny = 0 //if this gets set at spawn (super rare), the mob is considered "shiny" and will use the shiny icon_state and holographic cards - - //the icon file for mob_hunt stuff is 'icons/effects/mob_hunt.dmi' so reference that for the following vars - var/icon_state_normal = "placeholder" //the icon_state for this mob's normal version - var/icon_state_shiny = "placeholder" //the icon_state for this mob's rare shiney version - - var/is_trap = 0 //if this gets set, the mob is a booby-trap and will electrocute any players that dare attempt to catch it - -/datum/mob_hunt/New(set_trap = 0, turf/trap_turf = null, no_register = 0) - if(set_trap) - level = max_level - is_trap = 1 - spawn_point = trap_turf - else - level = rand(min_level, max_level) - if(prob(1) && prob(1)) - is_shiny = 1 - max_health = base_health + (level * health_multiplier) - cur_health = max_health - if(primary_type) - primary_type = new primary_type() - if(secondary_type) - secondary_type = new secondary_type() - if(no_register) //for booster pack cards - return - if(SSmob_hunt) - if(set_trap) - if(SSmob_hunt.register_trap(src)) - return - else if(select_spawn()) - if(SSmob_hunt.register_spawn(src)) - return - qdel(src) //if you reach this, the datum is just pure clutter, so delete it - -/datum/mob_hunt/proc/select_spawn() - var/list/possible_areas = get_possible_areas() - if(!length(possible_areas)) - log_admin("No possible areas to spawn [type] found. Possible code/mapping error?") - return 0 - while(length(possible_areas)) - //randomly select an area from our possible_areas list to try spawning in, then remove it from possible_areas so it won't get picked over and over forever. - var/spawn_area_path = pickweight(possible_areas) - var/area/spawn_area = locate(spawn_area_path) - possible_areas -= spawn_area_path - if(!spawn_area) - break - //clear and generate a fresh list of turfs in the selected area, weighted based on white/black lists - var/list/possible_turfs = get_possible_turfs(spawn_area) - if(!length(possible_turfs)) //If we don't have any possible turfs, this attempt was a failure. Try again. - continue - //if we got this far, we're spawning on this attempt, hooray! - spawn_point = pickweight(possible_turfs) - break - if(!spawn_point) - //if we get to this, we failed every attempt to find a suitable turf for EVERY area in our list of possible areas. DAMN. - log_admin("No acceptable turfs to spawn [type] on could be located. Possible code/mapping error, or someone replaced/destroyed all the acceptable turf types?") - return 0 - return 1 - -/datum/mob_hunt/proc/get_possible_areas() - var/list/possible_areas = list() - //setup, sets all station areas (and subtypes) to weight 1 - for(var/area/A in SSmapping.existing_station_areas) - if(istype(A, /area/holodeck)) //don't allow holodeck areas as possible spawns since it will allow it to spawn in the holodeck rooms on CC level as well - continue - if(A in possible_areas) - continue - for(var/areapath in typesof(A)) - possible_areas[areapath] = 1 - //primary type preferences - if(primary_type) - for(var/A in primary_type.area_whitelist) - for(var/areapath in typesof(A)) - possible_areas[areapath] += 4 - for(var/A in primary_type.area_blacklist) - for(var/areapath in typesof(A)) - possible_areas[areapath] -= 2 - //secondary type preferences - if(secondary_type) - for(var/A in secondary_type.area_whitelist) - for(var/areapath in typesof(A)) - possible_areas[areapath] += 4 - for(var/A in secondary_type.area_blacklist) - for(var/areapath in typesof(A)) - possible_areas[areapath] -= 2 - //mob preferences - for(var/A in area_whitelist) - for(var/areapath in typesof(A)) - possible_areas[areapath] += 4 - for(var/A in area_blacklist) - for(var/areapath in typesof(A)) - possible_areas[areapath] -= 2 - //weight check, remove negative or zero weight areas from the list, then return the list. - for(var/areapath in possible_areas) - //remove any areas that shouldn't be on the station-level - if(possible_areas[areapath] < 1) - possible_areas -= areapath - continue - return possible_areas - -/datum/mob_hunt/proc/get_possible_turfs(area/spawn_area) - if(!spawn_area) - return list() - var/list/possible_turfs = list() - //setup, sets all turfs in spawn_area to weight 1 - for(var/turf/T in spawn_area) - if(!is_station_level(T.z)) //mobs will only consider station-level turfs for spawning. Largely here so we won't have to worry about mapping errors or mobs on the derelict solars - continue - possible_turfs[T] = 1 - //primary type preferences - if(primary_type) - if(is_type_in_list(T, primary_type.turf_whitelist)) - possible_turfs[T] += 4 - if(is_type_in_list(T, primary_type.turf_blacklist)) - possible_turfs[T] -= 2 - //secondary type preferences - if(secondary_type) - if(is_type_in_list(T, secondary_type.turf_whitelist)) - possible_turfs[T] += 4 - if(is_type_in_list(T, secondary_type.turf_blacklist)) - possible_turfs[T] -= 2 - //mob preferences - if(is_type_in_list(T, turf_whitelist)) - possible_turfs[T] += 4 - if(is_type_in_list(T, turf_blacklist)) - possible_turfs[T] -= 2 - //weight check, remove negative or zero weight turfs from the list, then return the list - if(possible_turfs[T] < 1) - possible_turfs -= T - return possible_turfs - -/datum/mob_hunt/proc/calc_def_multiplier(datum/mob_type/attack_type) - if(!primary_type) - return 99 //typeless mobs are weak to everything since they shouldn't exist - if(!attack_type) //typeless attacks will return a multiplier of 1 in case we want to use this for calculating unmodified damage for some reason (UI maybe?) - return 1 - - var/multiplier = 1 - if(attack_type in primary_type.immunity) - return 0 //a single immunity negates all damage - else if(attack_type in primary_type.resistance) - multiplier *= 0.5 - else if(attack_type in primary_type.weakness) - multiplier *= 2 - else - multiplier *= 1 - - if(!secondary_type) //if we don't have a second type, we're done here - return multiplier - if(attack_type in secondary_type.immunity) - return 0 //a single immunity negates all damage - else if(attack_type in secondary_type.resistance) - multiplier *= 0.5 - else if(attack_type in secondary_type.weakness) - multiplier *= 2 - else - multiplier *= 1 - return multiplier - -/datum/mob_hunt/proc/take_damage(raw_damage, datum/mob_type/attack_type) - var/message = "" - var/multiplier = calc_def_multiplier(attack_type) - var/total_damage = raw_damage * multiplier - if(!cur_health) //it's already downed, quit hitting it - return null - if(!total_damage) - message += "The attack is completely ineffective! " - else - cur_health = max(cur_health - total_damage, 0) - switch(multiplier) - if(0) - message += "The attack is completely ineffective! " - if(0.25) - message += "It's barely effective... " - if(0.5) - message += "It's not very effective... " - if(2) - message += "It's super effective! " - if(4) - message += "It's ultra effective! " - if(99) - message += pick("REKT! ", "DUNKED! ", "DEFENSE BREAK! ", "WOMBO-COMBO'D!") - if(!cur_health) - message += "[nickname ? nickname : mob_name] is downed!" - return message - -/datum/mob_hunt/proc/get_raw_damage() - return (level * attack_multiplier) - -/datum/mob_hunt/proc/get_attack_type() - var/datum/mob_type/attack_type = primary_type - if(secondary_type && prob(40)) - attack_type = secondary_type - return attack_type - -/datum/mob_hunt/proc/gain_exp() - exp++ - var/message = "[nickname ? nickname : mob_name] gained EXP! ([exp] / [exp_to_level] EXP)" - if(exp >= exp_to_level) - message = levelup() - return message - -/datum/mob_hunt/proc/levelup() - var/message = "" - level++ - exp = 0 - if(level > max_level) //This is where we would trigger an evolution, when those are added (need to add evolved forms first) - level = max_level //for now though, we'll just cap them back at their max_level - message += "[nickname ? nickname : mob_name] can't get any stronger right now!" - else - max_health = base_health + (level * health_multiplier) - cur_health = max_health //full heal on level-up - message += "[nickname ? nickname : mob_name] has reached level [level]!" - return message - -/datum/mob_hunt/proc/get_type1() - if(!primary_type) - return "Typeless" - else - return primary_type.name - -/datum/mob_hunt/proc/get_type2() - if(!secondary_type) - return null - else - return secondary_type.name - - -/datum/mob_hunt/nemabug - mob_name = "Nemabug" - run_chance = 50 - min_level = 1 - max_level = 10 - primary_type = NANOMOB_TYPE_BUG - icon_state_normal = "nemabug" - icon_state_shiny = "nemabug_shiny" - lifetime = 6000 - -/datum/mob_hunt/stoutquill - mob_name = "Stoutquill" - run_chance = 50 - min_level = 5 - max_level = 15 - primary_type = NANOMOB_TYPE_ICE - icon_state_normal = "stoutquill" - icon_state_shiny = "stoutquill_shiny" - lifetime = 4500 - -/datum/mob_hunt/spectra - mob_name = "Spectra" - run_chance = 35 - min_level = 1 - max_level = 10 - primary_type = NANOMOB_TYPE_POISON - icon_state_normal = "spectra" - icon_state_shiny = "spectra_shiny" - lifetime = 6000 - -/datum/mob_hunt/dunny - mob_name = "Dunny" - run_chance = 35 - min_level = 1 - max_level = 10 - primary_type = NANOMOB_TYPE_FIRE - icon_state_normal = "dunny" - icon_state_shiny = "dunny_shiny" - lifetime = 6000 - -/datum/mob_hunt/buffsel - mob_name = "Buffsel" - run_chance = 35 - min_level = 1 - max_level = 10 - primary_type = NANOMOB_TYPE_ROCK - icon_state_normal = "buffsel" - icon_state_shiny = "buffsel_shiny" - lifetime = 6000 - -/datum/mob_hunt/quarrel - mob_name = "Quarrel" - run_chance = 35 - min_level = 1 - max_level = 10 - primary_type = NANOMOB_TYPE_NORMAL - icon_state_normal = "quarrel" - icon_state_shiny = "quarrel_shiny" - lifetime = 6000 - -/datum/mob_hunt/vulerrt - mob_name = "Vulerrt" - run_chance = 50 - min_level = 5 - max_level = 15 - primary_type = NANOMOB_TYPE_DARK - icon_state_normal = "vulerrt" - icon_state_shiny = "vulerrt_shiny" - turf_whitelist = list() - lifetime = 4500 - -/datum/mob_hunt/strudel - mob_name = "Strudel" - run_chance = 35 - min_level = 1 - max_level = 10 - primary_type = NANOMOB_TYPE_ELECTRIC - icon_state_normal = "strudel" - icon_state_shiny = "strudel_shiny" - lifetime = 4500 - -/datum/mob_hunt/folstick - mob_name = "Folstick" - run_chance = 35 - min_level = 1 - max_level = 10 - primary_type = NANOMOB_TYPE_WATER - icon_state_normal = "folstick" - icon_state_shiny = "folstick_shiny" - lifetime = 6000 - -/datum/mob_hunt/glimmerflare - mob_name = "Glimmerflare" - run_chance = 50 - min_level = 5 - max_level = 15 - primary_type = NANOMOB_TYPE_PSYCHIC - icon_state_normal = "glimmerflare" - icon_state_shiny = "glimmerflare_shiny" - lifetime = 4500 - -/datum/mob_hunt/leecoon - mob_name = "Leecoon" - run_chance = 35 - min_level = 1 - max_level = 10 - primary_type = NANOMOB_TYPE_GRASS - icon_state_normal = "leecoon" - icon_state_shiny = "leecoon_shiny" - lifetime = 6000 - -/datum/mob_hunt/halk - mob_name = "Halk" - run_chance = 35 - min_level = 1 - max_level = 10 - primary_type = NANOMOB_TYPE_FLYING - icon_state_normal = "halk" - icon_state_shiny = "halk_shiny" - lifetime = 6000 - -/datum/mob_hunt/gooby - mob_name = "Gooby" - run_chance = 65 - min_level = 5 - max_level = 20 - primary_type = NANOMOB_TYPE_ELECTRIC - secondary_type = NANOMOB_TYPE_BUG - icon_state_normal = "gooby" - icon_state_shiny = "gooby_shiny" - lifetime = 3000 - -/datum/mob_hunt/pandoom - mob_name = "Pandoom" - run_chance = 50 - min_level = 5 - max_level = 15 - primary_type = NANOMOB_TYPE_GHOST - icon_state_normal = "pandoom" - icon_state_shiny = "pandoom_shiny" - lifetime = 4500 - -/datum/mob_hunt/relish - mob_name = "Relish" - run_chance = 65 - min_level = 5 - max_level = 20 - primary_type = NANOMOB_TYPE_FIRE - secondary_type = NANOMOB_TYPE_GROUND - icon_state_normal = "relish" - icon_state_shiny = "relish_shiny" - lifetime = 3000 - -/datum/mob_hunt/xofine - mob_name = "Xofine" - run_chance = 50 - min_level = 5 - max_level = 10 - primary_type = NANOMOB_TYPE_FIRE - secondary_type = NANOMOB_TYPE_NORMAL - icon_state_normal = "xofine" - icon_state_shiny = "xofine_shiny" - lifetime = 3000 - -/datum/mob_hunt/gitten - mob_name = "Gitten" - run_chance = 65 - min_level = 5 - max_level = 20 - primary_type = NANOMOB_TYPE_WATER - secondary_type = NANOMOB_TYPE_POISON - icon_state_normal = "gitten" - icon_state_shiny = "gitten_shiny" - lifetime = 3000 - -/datum/mob_hunt/nai - mob_name = "Nai" - run_chance = 65 - min_level = 5 - max_level = 20 - primary_type = NANOMOB_TYPE_GRASS - secondary_type = NANOMOB_TYPE_NORMAL - icon_state_normal = "nai" - icon_state_shiny = "nai_shiny" - lifetime = 3000 - -/datum/mob_hunt/pyroghast - mob_name = "Pyroghast" - run_chance = 65 - min_level = 5 - max_level = 20 - primary_type = NANOMOB_TYPE_FIRE - secondary_type = NANOMOB_TYPE_GHOST - icon_state_normal = "pyroghast" - icon_state_shiny = "pyroghast" - lifetime = 4500 - -/datum/mob_hunt/starslam - mob_name = "Starslam" - run_chance = 65 - min_level = 5 - max_level = 20 - primary_type = NANOMOB_TYPE_FIGHTING - secondary_type = NANOMOB_TYPE_ICE - icon_state_normal = "starslam" - icon_state_shiny = "starslam_shiny" - lifetime = 2500 - -/datum/mob_hunt/pheron - mob_name = "Pheron" - run_chance = 85 - min_level = 10 - max_level = 20 - primary_type = NANOMOB_TYPE_BLUESPACE - icon_state_normal = "pheron" - icon_state_shiny = "pheron_shiny" - area_blacklist = list() - turf_blacklist = list() - area_whitelist = list() - turf_whitelist = list() - lifetime = 2000 diff --git a/code/modules/arcade/mob_hunt/mob_type_datums.dm b/code/modules/arcade/mob_hunt/mob_type_datums.dm deleted file mode 100644 index 1789ac679126..000000000000 --- a/code/modules/arcade/mob_hunt/mob_type_datums.dm +++ /dev/null @@ -1,241 +0,0 @@ - -/datum/mob_type - var/name = "Typeless" - var/list/weakness = list() - var/list/resistance = list() - var/list/immunity = list() - - //Type-based spawn preferences (to eliminate copy-pasting the same area and turf lists on each mob type) - var/list/area_blacklist = list() //areas to be avoided - var/list/area_whitelist = list() //areas to be more preferred - var/list/turf_blacklist = list() //turf types to be avoided - var/list/turf_whitelist = list() //turf types to be more preferred - -//Type defines, to avoid spelling mistakes -/datum/mob_type/fire - name = "Fire" - weakness = list(NANOMOB_TYPE_WATER, - NANOMOB_TYPE_ROCK, - NANOMOB_TYPE_GROUND) - resistance = list(NANOMOB_TYPE_BUG, - NANOMOB_TYPE_FIRE, - NANOMOB_TYPE_GRASS, - NANOMOB_TYPE_ICE, - NANOMOB_TYPE_STEEL) - area_blacklist = list(/area/station/public/toilet, - /area/station/science/server/coldroom) - area_whitelist = list(/area/station/maintenance/turbine, - /area/station/maintenance/incinerator, - /area/station/service/kitchen) - turf_blacklist = list(/turf/simulated/floor/beach/water) - -/datum/mob_type/water - name = "Water" - weakness = list(NANOMOB_TYPE_ELECTRIC, - NANOMOB_TYPE_GRASS) - resistance = list(NANOMOB_TYPE_FIRE, - NANOMOB_TYPE_ICE, - NANOMOB_TYPE_STEEL, - NANOMOB_TYPE_WATER) - area_blacklist = list(/area/station/maintenance/turbine, - /area/station/maintenance/incinerator, - /area/station/service/kitchen) - area_whitelist = list(/area/station/public/toilet) - turf_whitelist = list(/turf/simulated/floor/beach/water) - -/datum/mob_type/grass - name = "Grass" - weakness = list(NANOMOB_TYPE_FIRE, - NANOMOB_TYPE_BUG, - NANOMOB_TYPE_POISON, - NANOMOB_TYPE_ICE, - NANOMOB_TYPE_FLYING) - resistance = list(NANOMOB_TYPE_WATER, - NANOMOB_TYPE_GRASS, - NANOMOB_TYPE_ELECTRIC, - NANOMOB_TYPE_GROUND) - area_blacklist = list(/area/station/science) - area_whitelist = list(/area/station/service/hydroponics, - /area/station/public/construction) // dont ask me why grass types can appear in construction sites, I'm just migrating areas - turf_whitelist = list(/turf/simulated/floor/grass) - -/datum/mob_type/electric - name = "Electric" - weakness = list(NANOMOB_TYPE_GROUND) - resistance = list(NANOMOB_TYPE_ELECTRIC, - NANOMOB_TYPE_FLYING, - NANOMOB_TYPE_STEEL) - area_whitelist = list(/area/station/engineering/engine, - /area/station/science/server, - /area/station/maintenance, - /area/station/turret_protected/ai, - /area/station/turret_protected/ai_upload, - /area/station/turret_protected/aisat/interior, - /area/station/aisat, - /area/station/science/robotics) - turf_whitelist = list(/turf/simulated/floor/bluegrid) - -/datum/mob_type/ground - name = "Ground" - weakness = list(NANOMOB_TYPE_WATER, - NANOMOB_TYPE_GRASS, - NANOMOB_TYPE_ICE) - resistance = list(NANOMOB_TYPE_ROCK, - NANOMOB_TYPE_POISON) - immunity = list(NANOMOB_TYPE_ELECTRIC) - -/datum/mob_type/rock - name = "Rock" - weakness = list(NANOMOB_TYPE_WATER, - NANOMOB_TYPE_GRASS, - NANOMOB_TYPE_GROUND, - NANOMOB_TYPE_FIGHTING, - NANOMOB_TYPE_STEEL) - resistance = list(NANOMOB_TYPE_FIRE, - NANOMOB_TYPE_FLYING, - NANOMOB_TYPE_POISON, - NANOMOB_TYPE_NORMAL) - area_whitelist = list(/area/station/supply, - /area/station/maintenance/disposal) - turf_whitelist = list(/turf/simulated/wall, - /turf/simulated/floor/mineral) - -/datum/mob_type/bug - name = "Bug" - weakness = list(NANOMOB_TYPE_FIRE, - NANOMOB_TYPE_ROCK, - NANOMOB_TYPE_FLYING) - resistance = list(NANOMOB_TYPE_GRASS, - NANOMOB_TYPE_GROUND, - NANOMOB_TYPE_FIGHTING) - area_blacklist = list(/area/station/science) - area_whitelist = list(/area/station/service/hydroponics, - /area/station/public/construction) // dont ask me why bug types can appear in construction sites, I'm just migrating areas - turf_whitelist = list(/turf/simulated/floor/grass) - -/datum/mob_type/poison - name = "Poison" - weakness = list(NANOMOB_TYPE_GROUND, - NANOMOB_TYPE_PSYCHIC) - resistance = list(NANOMOB_TYPE_GRASS, - NANOMOB_TYPE_BUG, - NANOMOB_TYPE_POISON, - NANOMOB_TYPE_FIGHTING) - area_blacklist = list(/area/station/medical, - /area/station/service/janitor) - area_whitelist = list(/area/station/medical/virology, - /area/station/science, - /area/station/command/office/rd, - /area/station/maintenance/asmaint2) - -/datum/mob_type/normal - name = "Normal" - weakness = list(NANOMOB_TYPE_FIGHTING) - immunity = list(NANOMOB_TYPE_GHOST) - -/datum/mob_type/fighting - name = "Fighting" - weakness = list(NANOMOB_TYPE_PSYCHIC, - NANOMOB_TYPE_FLYING) - resistance = list(NANOMOB_TYPE_ROCK, - NANOMOB_TYPE_BUG, - NANOMOB_TYPE_DARK) - area_blacklist = list(/area/station/medical) - area_whitelist = list(/area/station/service/bar, - /area/station/public/fitness, - /area/station/security) - turf_whitelist = list(/turf/simulated/floor/wood) - -/datum/mob_type/psychic - name = "Psychic" - weakness = list(NANOMOB_TYPE_BUG, - NANOMOB_TYPE_GHOST, - NANOMOB_TYPE_DARK) - resistance = list(NANOMOB_TYPE_FIGHTING, - NANOMOB_TYPE_PSYCHIC) - area_blacklist = list(/area/station/science, - /area/station/command/office/rd, - /area/station/maintenance/asmaint2, - /area/station/command/teleporter, - /area/station/command/gateway) - area_whitelist = list(/area/station/service/library, - /area/station/service/chapel, - /area/station/medical/psych) - -/datum/mob_type/ghost - name = "Ghost" - weakness = list(NANOMOB_TYPE_GHOST, - NANOMOB_TYPE_DARK) - resistance = list(NANOMOB_TYPE_BUG, - NANOMOB_TYPE_POISON) - immunity = list(NANOMOB_TYPE_NORMAL, - NANOMOB_TYPE_FIGHTING) - area_whitelist = list(/area/station/medical/morgue, - /area/station/service/chapel, - /area/station/medical/cloning) - -/datum/mob_type/ice - name = "Ice" - weakness = list(NANOMOB_TYPE_FIRE, - NANOMOB_TYPE_ROCK, - NANOMOB_TYPE_FIGHTING, - NANOMOB_TYPE_STEEL) - resistance = list(NANOMOB_TYPE_ICE) - area_blacklist = list(/area/station/maintenance/turbine, - /area/station/maintenance/incinerator, - /area/station/service/kitchen) - area_whitelist = list(/area/station/science/server/coldroom) - -/datum/mob_type/flying - name = "Flying" - weakness = list(NANOMOB_TYPE_ELECTRIC, - NANOMOB_TYPE_ROCK, - NANOMOB_TYPE_ICE) - resistance = list(NANOMOB_TYPE_GRASS, - NANOMOB_TYPE_BUG, - NANOMOB_TYPE_FIGHTING) - immunity = list(NANOMOB_TYPE_GROUND) - area_blacklist = list(/area/station/maintenance) - area_whitelist = list(/area/station/hallway) - -/datum/mob_type/bluespace - name = "Bluespace" - weakness = list(NANOMOB_TYPE_ICE, - NANOMOB_TYPE_BLUESPACE) - resistance = list(NANOMOB_TYPE_FIRE, - NANOMOB_TYPE_WATER, - NANOMOB_TYPE_GRASS, - NANOMOB_TYPE_ELECTRIC) - -/datum/mob_type/dark - name = "Dark" - weakness = list(NANOMOB_TYPE_BUG, - NANOMOB_TYPE_FIGHTING) - resistance = list(NANOMOB_TYPE_GHOST, - NANOMOB_TYPE_DARK) - immunity = list(NANOMOB_TYPE_PSYCHIC) - area_blacklist = list(/area/station/engineering/solar, - /area/station/maintenance/auxsolarport, - /area/station/maintenance/starboardsolar, - /area/station/maintenance/portsolar, - /area/station/maintenance/auxsolarstarboard, - /area/station/service/clown) - area_whitelist = list(/area/station/maintenance, - /area/station/service/mime) - turf_blacklist = list(/turf/simulated/floor/light) - -/datum/mob_type/steel - name = "Steel" - weakness = list(NANOMOB_TYPE_FIRE, - NANOMOB_TYPE_GROUND, - NANOMOB_TYPE_FIGHTING) - resistance = list(NANOMOB_TYPE_GRASS, - NANOMOB_TYPE_ROCK, - NANOMOB_TYPE_BUG, - NANOMOB_TYPE_NORMAL, - NANOMOB_TYPE_PSYCHIC, - NANOMOB_TYPE_ICE, - NANOMOB_TYPE_FLYING, - NANOMOB_TYPE_BLUESPACE, - NANOMOB_TYPE_STEEL) - immunity = list(NANOMOB_TYPE_POISON) diff --git a/code/modules/arcade/prize_datums.dm b/code/modules/arcade/prize_datums.dm index 40d8f4d54e30..7e9481bceee8 100644 --- a/code/modules/arcade/prize_datums.dm +++ b/code/modules/arcade/prize_datums.dm @@ -275,12 +275,6 @@ GLOBAL_DATUM_INIT(global_prizes, /datum/prizes, new()) typepath = /obj/item/clothing/under/syndicate/tacticool cost = 90 -/datum/prize_item/nanomob_booster - name = "Nano-Mob Hunter Trading Card Booster Pack" - desc = "Contains 6 random Nano-Mob Hunter Trading Cards. May contain a holographic card!" - typepath = /obj/item/storage/box/nanomob_booster_pack - cost = 100 - /datum/prize_item/fakespell name = "Fake Spellbook" desc = "Perform magic! Astound your friends! Get mistaken for an enemy of the corporation!" diff --git a/code/modules/asset_cache/assets/asset_mob_hunt.dm b/code/modules/asset_cache/assets/asset_mob_hunt.dm deleted file mode 100644 index ed1f7494e332..000000000000 --- a/code/modules/asset_cache/assets/asset_mob_hunt.dm +++ /dev/null @@ -1,7 +0,0 @@ -/datum/asset/simple/mob_hunt/register() - for(var/state in icon_states('icons/effects/mob_hunt.dmi')) - if(state == "Placeholder") - continue - assets["[state].png"] = icon('icons/effects/mob_hunt.dmi', state) - - return ..() diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 6330afcfa705..e66ed538b06e 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -456,6 +456,7 @@ display_job_bans(TRUE) if(check_rights(R_DEBUG|R_VIEWRUNTIMES, FALSE, mob)) winset(src, "debugmcbutton", "is-disabled=false") + winset(src, "profilecode", "is-disabled=false") /client/proc/is_connecting_from_localhost() var/static/list/localhost_addresses = list("127.0.0.1", "::1") @@ -1230,6 +1231,18 @@ if("Set-Tab") stat_tab = payload["tab"] SSstatpanels.immediate_send_stat_data(src) + if("Listedturf-Scroll") + if(payload["min"] == payload["max"]) + // Not properly loaded yet, send the default set. + SSstatpanels.refresh_client_obj_view(src) + else + SSstatpanels.refresh_client_obj_view(src, payload["min"], payload["max"]) + // Uncomment to enable log_debug in stat panel code. + // Disabled normally due to HREF exploit concerns. + //if("Statpanel-Debug") + // log_debug(payload) + if("Resend-Asset") + SSassets.transport.send_assets(src, list(payload)) if("Debug-Stat-Entry") var/stat_item = locateUID(payload["stat_item_uid"]) if(!check_rights(R_DEBUG | R_VIEWRUNTIMES) || !stat_item) diff --git a/code/modules/client/preference/loadout/loadout_general.dm b/code/modules/client/preference/loadout/loadout_general.dm index 2549eff6d9c1..e243d1318742 100644 --- a/code/modules/client/preference/loadout/loadout_general.dm +++ b/code/modules/client/preference/loadout/loadout_general.dm @@ -162,11 +162,6 @@ path = /obj/item/paicard cost = 2 -/datum/gear/mob_hunt_game - display_name = "Nano-Mob Hunter GO! Cartridge" - path = /obj/item/cartridge/mob_hunt_game - cost = 2 - ////////////////////// // Mugs // ////////////////////// diff --git a/code/modules/economy/merch_items.dm b/code/modules/economy/merch_items.dm index 4b2c97487d5a..b15a0eb6b303 100644 --- a/code/modules/economy/merch_items.dm +++ b/code/modules/economy/merch_items.dm @@ -47,13 +47,6 @@ cost = 100 category = MERCH_CAT_TOY -/datum/merch_item/nanomob_booster - name = "Nano-Mob Hunter Trading Card Booster Pack" - desc = "Contains 6 random Nano-Mob Hunter Trading Cards. May contain a holographic card!" - typepath = /obj/item/storage/box/nanomob_booster_pack - cost = 125 - category = MERCH_CAT_TOY - /datum/merch_item/crayons name = "Crayons" desc = "Let security know how they're doing by scrawling love notes all over their hallways." diff --git a/code/modules/events/traders.dm b/code/modules/events/traders.dm index b0eb67f4df5a..684772392d1e 100644 --- a/code/modules/events/traders.dm +++ b/code/modules/events/traders.dm @@ -27,9 +27,9 @@ GLOBAL_LIST_INIT(unused_trade_stations, list("sol")) /datum/traders/nian) if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED) - GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been denied docking permission due to the heightened security alert aboard [station_name()].", "Trader Shuttle Docking Request Refused") + GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been denied docking permission due to the heightened security alert aboard [station_name()].", "Trader Shuttle Docking Request Refused", 'sound/AI/traderdeny.ogg') return - GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been granted docking permission at [station_name()] arrivals port 4.", "Trader Shuttle Docking Request Accepted") + GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been granted docking permission at [station_name()] arrivals port 4.", "Trader Shuttle Docking Request Accepted", 'sound/AI/tradergranted.ogg') /datum/event/traders/start() @@ -48,7 +48,7 @@ GLOBAL_LIST_INIT(unused_trade_stations, list("sol")) /datum/traders/nian) if(SSsecurity_level.get_current_level_as_number() >= SEC_LEVEL_RED) - GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been denied docking permission due to the heightened security alert aboard [station_name()].", "Trader Shuttle Docking Request Refused") + GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been denied docking permission due to the heightened security alert aboard [station_name()].", "Trader Shuttle Docking Request Refused", 'sound/AI/traderdeny.ogg') // if the docking request was refused, fire another major event in 60 seconds var/datum/event_container/EC = SSevents.event_containers[EVENT_LEVEL_MAJOR] EC.next_event_time = world.time + (60 * 10) @@ -102,7 +102,7 @@ GLOBAL_LIST_INIT(unused_trade_stations, list("sol")) greet_trader(M, T) success_spawn = TRUE if(success_spawn) - GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been granted docking permission at [station_name()] arrivals port 4.", "Trader Shuttle Docking Request Accepted") + GLOB.minor_announcement.Announce("A trading shuttle from [T.trader_location] has been granted docking permission at [station_name()] arrivals port 4.", "Trader Shuttle Docking Request Accepted", 'sound/AI/tradergranted.ogg') else GLOB.unused_trade_stations += station // Return the station to the list of usable stations. diff --git a/code/modules/food_and_drinks/food/snacks.dm b/code/modules/food_and_drinks/food/snacks.dm index cccfab34edc9..4f47e4a8863a 100644 --- a/code/modules/food_and_drinks/food/snacks.dm +++ b/code/modules/food_and_drinks/food/snacks.dm @@ -180,6 +180,8 @@ . += "Alt-click to put something small inside." /obj/item/food/snacks/sliceable/AltClick(mob/user) + if(!Adjacent(user)) + return var/obj/item/I = user.get_active_hand() if(!I) return diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index 9c8819b2a11d..59b6e008be56 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -696,7 +696,6 @@ Pass a positive integer as an argument to override a bot's default speed. - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //Patrol and summon code! //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index 0ecf75e9a7f3..fa3ab06c3c73 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -81,6 +81,7 @@ target = null oldloc = null area_locked = null + update_icon() /mob/living/simple_animal/bot/cleanbot/set_custom_texts() text_hack = "You corrupt [name]'s cleaning software." @@ -173,6 +174,7 @@ oldloc = loc /mob/living/simple_animal/bot/cleanbot/proc/assign_area() + auto_patrol = FALSE // Don't want autopatrol if we are area locked if(area_locked) area_locked = null else diff --git a/code/modules/pda/PDA.dm b/code/modules/pda/PDA.dm index 231dafbef73b..e21c00727b2e 100644 --- a/code/modules/pda/PDA.dm +++ b/code/modules/pda/PDA.dm @@ -312,9 +312,6 @@ GLOBAL_LIST_EMPTY(PDAs) add_pen(C) to_chat(user, "You slide \the [C] into \the [src].") playsound(src, 'sound/machines/pda_button1.ogg', 50, TRUE) - else if(istype(C, /obj/item/nanomob_card)) - if(cartridge && istype(cartridge, /obj/item/cartridge/mob_hunt_game)) - cartridge.attackby(C, user, params) /obj/item/pda/proc/add_pen(obj/item/P) P.forceMove(src) diff --git a/code/modules/pda/cart.dm b/code/modules/pda/cart.dm index 4a66081f9563..75deb8f685da 100644 --- a/code/modules/pda/cart.dm +++ b/code/modules/pda/cart.dm @@ -306,40 +306,3 @@ messenger_plugins = list( new /datum/data/pda/messenger_plugin/virus/frame ) - -/obj/item/cartridge/mob_hunt_game - name = "Nano-Mob Hunter GO! Cartridge" - desc = "The hit new PDA game that lets you track down and capture your favorite Nano-Mobs living in your world!" - icon_state = "cart-eye" - programs = list( - new /datum/data/pda/app/mob_hunter_game - ) - -/obj/item/cartridge/mob_hunt_game/examine(mob/user) - . = ..() - if(emagged) - if(isAntag(user)) - . += "This copy of Nano-Mob Hunter GO! has been hacked to allow the creation of trap mobs which will cause any PDA that attempts to capture it to shock anyone holding it. Hacked copies of the game will not trigger the trap. Provided you actually find someone playing nanomobs, that is." - -/obj/item/cartridge/mob_hunt_game/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/nanomob_card)) - var/obj/item/nanomob_card/card = O - var/datum/data/pda/app/mob_hunter_game/my_game = programs[1] - - if(my_game.register_capture(card.mob_data)) - to_chat(user, "Transfer successful!") - qdel(card) - else - to_chat(user, "Transfer failed. Could not read mob data from card.") - - else - ..() - -/obj/item/cartridge/mob_hunt_game/emag_act(mob/user) - if(!emagged) - emagged = TRUE - var/datum/data/pda/app/mob_hunter_game/my_game = programs[1] - my_game.hacked = TRUE - to_chat(user, "TR4P_M45T3R.mod successfully initialized. ToS violated. User Agreement nullified. Gotta pwn them all.") - to_chat(user, "You can now create trapped versions of any mob in your collection that will damage hunters who attempt to capture it.") - return TRUE diff --git a/code/modules/pda/mob_hunt_game_app.dm b/code/modules/pda/mob_hunt_game_app.dm deleted file mode 100644 index 73eab8cf1a52..000000000000 --- a/code/modules/pda/mob_hunt_game_app.dm +++ /dev/null @@ -1,205 +0,0 @@ - -/* - This stuff isn't included with in core_apps.dm because it is so distinct and would end up making the file huge (more than it already is). - I put it in it's own file in order to help it stay organized and easier to locate the procs and such. - For reference, the other Nano-mob Hunter GO files are in /code/game/modules/arcade/mob_hunt -*/ - -/datum/data/pda/app/mob_hunter_game - name = "Nano-Mob Hunter GO" - icon = "gamepad" - template = "pda_mob_hunt" - category = "Games" - - var/list/my_collection = list() - var/current_index = 0 - var/connected = FALSE - var/hacked = FALSE //if set, this cartridge is able to spawn trap mobs from its collection (set via emag_act on the cartridge) - var/catch_mod = 0 //used to adjust the likelihood of a mob running from this client, a negative value means it is less likely to run (support for temporary bonuses) - var/wild_captures = 0 //used to track the total number of mobs captured from the wild (does not count card mobs) by this client - var/scan_range = 3 //maximum distance (in tiles) from which the client can reveal nearby mobs - -/datum/data/pda/app/mob_hunter_game/start() - ..() - START_PROCESSING(SSobj, pda) - -/datum/data/pda/app/mob_hunter_game/stop() - ..() - disconnect("Program Terminated") - STOP_PROCESSING(SSobj, pda) - -/datum/data/pda/app/mob_hunter_game/Destroy() - if(pda) - STOP_PROCESSING(SSobj, pda) - SSmob_hunt.connected_clients -= src - return ..() - -/datum/data/pda/app/mob_hunter_game/proc/scan_nearby() - if(!SSmob_hunt || !connected) - return - for(var/turf/T in range(scan_range, get_turf(pda))) - for(var/obj/effect/nanomob/N in T.contents) - if(src in N.clients_encountered) - //hide the mob - N.conceal() - else - //reveal the mob - N.reveal() - -/datum/data/pda/app/mob_hunter_game/proc/reconnect() - if(!SSmob_hunt || !SSmob_hunt.server_status || connected) - //show a message about the server being unavailable (because it doesn't exist / didn't get set to the global var / is offline) - return 0 - SSmob_hunt.connected_clients += src - connected = TRUE - if(pda) - pda.atom_say("Connection established. Capture all of the mobs, [pda.owner ? pda.owner : "hunter"]!") - return 1 - -/datum/data/pda/app/mob_hunter_game/proc/get_player() - if(!pda) - return null - if(ishuman(pda.loc)) - var/mob/living/carbon/human/H = pda.loc - return H - return null - -/datum/data/pda/app/mob_hunter_game/proc/disconnect(reason = null) - if(!SSmob_hunt || !connected) - return - SSmob_hunt.connected_clients -= src - for(var/obj/effect/nanomob/N in (SSmob_hunt.normal_spawns + SSmob_hunt.trap_spawns)) - N.conceal(list(get_player())) - connected = FALSE - //show a disconnect message if we were disconnected involuntarily (reason argument provided) - if(pda && reason) - pda.atom_say("Disconnected from server. Reason: [reason].") - -/datum/data/pda/app/mob_hunter_game/program_process() - if(!SSmob_hunt || !connected) - return - scan_nearby() - -/datum/data/pda/app/mob_hunter_game/proc/register_capture(datum/mob_hunt/captured, wild = FALSE) - if(!captured) - return FALSE - my_collection += captured - RegisterSignal(captured, COMSIG_PARENT_QDELETING, PROC_REF(remove_mob)) - if(wild) - wild_captures++ - return TRUE - -/datum/data/pda/app/mob_hunter_game/update_ui(mob/user, list/data) - if(!SSmob_hunt || !(src in SSmob_hunt.connected_clients)) - data["connected"] = 0 - else - data["connected"] = 1 - data["wild_captures"] = wild_captures - data["no_collection"] = 0 - if(!length(my_collection)) - data["no_collection"] = 1 - return - var/datum/mob_hunt/mob_info - if(!current_index) - current_index = 1 - if(current_index > length(my_collection)) - current_index = 1 - if(current_index < 1) - current_index = length(my_collection) - mob_info = my_collection[current_index] - var/list/entry = list( - "nickname" = mob_info.nickname, - "real_name" = mob_info.mob_name, - "level" = mob_info.level, - "type1" = mob_info.get_type1(), - "type2" = mob_info.get_type2(), - "sprite" = "[mob_info.icon_state_normal].png", - "is_hacked" = hacked - ) - if(mob_info.is_shiny) - entry["sprite"] = "[mob_info.icon_state_shiny].png" - data["entry"] = entry - -/datum/data/pda/app/mob_hunter_game/proc/assign_nickname() - if(!length(my_collection)) - return - var/datum/mob_hunt/mob_info = my_collection[current_index] - var/old_name = mob_info.mob_name - if(mob_info.nickname) - old_name = mob_info.nickname - mob_info.nickname = tgui_input_text(usr, "Give a nickname to [old_name]?", "Nickname", old_name) - -/datum/data/pda/app/mob_hunter_game/proc/release() - if(!length(my_collection)) - return - if(tgui_alert(usr, "Are you sure you want to release this mob back into the wild?", "Confirm Release", list("Yes", "No")) == "Yes") - remove_mob() - -/datum/data/pda/app/mob_hunter_game/proc/print_card() - if(!pda || !length(my_collection)) - return - var/obj/item/nanomob_card/card = new/obj/item/nanomob_card(null) - var/datum/mob_hunt/mob_info = my_collection[current_index] - card.mob_data = mob_info - card.update_info() - card.forceMove(get_turf(pda)) - remove_mob() - -/** - * Removes a Nanomob from the [my_collection] list. - * - * The Nanomob that is currently selected in the app ([current_index]) will be removed from the list unless a `mob_override` argument is given, in which case that will be removed instead. - * - * Arguments: - * * mob_override - A specific Nanomob to remove from the list. (Optional) - */ -/datum/data/pda/app/mob_hunter_game/proc/remove_mob(datum/mob_hunt/mob_override = null) - SIGNAL_HANDLER - if(!length(my_collection)) - return - - if(mob_override) - my_collection -= mob_override - else - my_collection -= my_collection[current_index] - - var/collection_length = length(my_collection) - if(current_index > collection_length) - current_index = collection_length - -/datum/data/pda/app/mob_hunter_game/proc/set_trap() - if(!length(my_collection) || !pda || !hacked) - return - var/datum/mob_hunt/bait = my_collection[current_index] - bait = bait.type - new bait(1, get_turf(pda)) - -/datum/data/pda/app/mob_hunter_game/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) - if(..()) - return - - if(!pda.silent) - playsound(pda, 'sound/machines/terminal_select.ogg', 15, TRUE) - - . = TRUE - switch(action) - if("Rename") - assign_nickname() - if("Release") - release() - if("Next") - current_index++ - if(current_index > length(my_collection)) - current_index = 1 - if("Prev") - current_index-- - if(current_index < 1) - current_index = length(my_collection) - if("Reconnect") - reconnect() - if("Disconnect") - disconnect() - if("Transfer") - print_card() - if("Set_Trap") - set_trap() diff --git a/code/modules/pda/pda_tgui.dm b/code/modules/pda/pda_tgui.dm index 27310410d31e..96c1a8f52961 100644 --- a/code/modules/pda/pda_tgui.dm +++ b/code/modules/pda/pda_tgui.dm @@ -60,11 +60,6 @@ return data -/obj/item/pda/ui_assets(mob/user) - return list( - get_asset_datum(/datum/asset/simple/mob_hunt) - ) - // Yes the stupid amount of args here is important, see L102 /obj/item/pda/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) if(..()) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 7baf9bb6cc79..b4b071e6f616 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -67,22 +67,14 @@ var/flight_x_offset = 0 var/flight_y_offset = 0 - //Zooming - var/zoomable = FALSE //whether the gun generates a Zoom action on creation - var/zoomed = FALSE //Zoom toggle - var/zoom_amt = 3 //Distance in TURFs to move the user's screen forward (the "zoom" effect) - var/datum/action/toggle_scope_zoom/azoom - /obj/item/gun/Initialize(mapload) . = ..() - build_zooming() ADD_TRAIT(src, TRAIT_CAN_POINT_WITH, ROUNDSTART_TRAIT) appearance_flags |= KEEP_TOGETHER /obj/item/gun/Destroy() QDEL_NULL(bayonet) QDEL_NULL(chambered) - QDEL_NULL(azoom) QDEL_NULL(gun_light) return ..() @@ -154,6 +146,10 @@ /obj/item/gun/afterattack(atom/target, mob/living/user, flag, params) if(firing_burst) return + if(SEND_SIGNAL(src, COMSIG_GUN_TRY_FIRE, user, target, flag, params) & COMPONENT_CANCEL_GUN_FIRE) + return + if(SEND_SIGNAL(src, COMSIG_MOB_TRY_FIRE, user, target, flag, params) & COMPONENT_CANCEL_GUN_FIRE) + return if(flag) //It's adjacent, is the user, or is on the user's person if(target in user.contents) //can't shoot stuff inside us. return @@ -414,11 +410,6 @@ visible_message("[src]'s light fades and turns off.") -/obj/item/gun/dropped(mob/user) - ..() - zoom(user,FALSE) - if(azoom) - azoom.Remove(user) /obj/item/gun/AltClick(mob/user) ..() @@ -485,93 +476,8 @@ process_fire(target, user, 1, params) -///////////// -// ZOOMING // -///////////// - -/datum/action/toggle_scope_zoom - name = "Toggle Scope" - check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING - button_icon_state = "sniper_zoom" - var/obj/item/gun/gun = null - -/datum/action/toggle_scope_zoom/Destroy() - gun = null - return ..() - -/datum/action/toggle_scope_zoom/Trigger(left_click) - gun.zoom(owner) - -/datum/action/toggle_scope_zoom/IsAvailable() - . = ..() - if(!. && gun) - gun.zoom(owner, FALSE) - -/datum/action/toggle_scope_zoom/Remove(mob/living/L) - gun.zoom(L, FALSE) - ..() - -/obj/item/gun/proc/zoom(mob/living/user, forced_zoom) - if(!user || !user.client) - return - - switch(forced_zoom) - if(FALSE) - zoomed = FALSE - if(TRUE) - zoomed = TRUE - else - zoomed = !zoomed - - if(zoomed) - var/_x = 0 - var/_y = 0 - switch(user.dir) - if(NORTH) - _y = zoom_amt - if(EAST) - _x = zoom_amt - if(SOUTH) - _y = -zoom_amt - if(WEST) - _x = -zoom_amt - - user.client.pixel_x = world.icon_size*_x - user.client.pixel_y = world.icon_size*_y - else - user.client.pixel_x = 0 - user.client.pixel_y = 0 - - -//Proc, so that gun accessories/scopes/etc. can easily add zooming. -/obj/item/gun/proc/build_zooming() - if(azoom) - return - - if(zoomable) - azoom = new() - azoom.gun = src - RegisterSignal(src, COMSIG_ITEM_EQUIPPED, PROC_REF(ZoomGrantCheck)) - -/** - * Proc which will be called when the gun receives the `COMSIG_ITEM_EQUIPPED` signal. - * - * This happens if the mob picks up the gun, or equips it to any of their slots. - * If the slot is anything other than either of their hands (such as the back slot), un-zoom them, and `Remove` the zoom action button from the mob. - * Otherwise, `Grant` the mob the zoom action button. - * - * Arguments: - * * source - the gun that got equipped, which is `src`. - * * user - the mob equipping the gun. - * * slot - the slot the gun is getting equipped to. - */ -/obj/item/gun/proc/ZoomGrantCheck(datum/source, mob/user, slot) - // Checks if the gun got equipped into either of the user's hands. - if(slot != SLOT_HUD_RIGHT_HAND && slot != SLOT_HUD_LEFT_HAND) - // If its not in their hands, un-zoom, and remove the zoom action button. - zoom(user, FALSE) - azoom.Remove(user) - return FALSE +/obj/item/gun/proc/on_scope_success() + return - // The gun is equipped in their hands, give them the zoom ability. - azoom.Grant(user) +/obj/item/gun/proc/on_scope_end() + return diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index 3d194b08510b..fddd8eacaf40 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -126,39 +126,21 @@ weapon_weight = WEAPON_HEAVY origin_tech = "combat=6;magnets=6;powerstorage=4" ammo_type = list(/obj/item/ammo_casing/energy/laser/sniper, /obj/item/ammo_casing/energy/laser/sniper/pierce) - zoomable = TRUE - zoom_amt = 7 shaded_charge = TRUE - /// Is the scope fully online or not? - var/scope_active = FALSE - var/stored_dir execution_speed = 8 SECONDS -/obj/item/gun/energy/lwap/zoom(mob/living/user, forced_zoom) +/obj/item/gun/energy/lwap/Initialize(mapload) . = ..() - if(!ishuman(user)) - return - var/mob/living/carbon/human/H = user - stored_dir = H.dir - if(scope_active && !zoomed) - select_fire(H) - H.remove_status_effect(STATUS_EFFECT_LWAPSCOPE) - scope_active = FALSE - return - if(zoomed && do_after(user, 3 SECONDS, target = src)) - if(zoomed && !scope_active) //We check after to be sure. - scope_active = TRUE - to_chat(user, "SCOPE_CREEPER_[rand(1, 9999)] Online.") - select_fire(H) - H.apply_status_effect(STATUS_EFFECT_LWAPSCOPE, stored_dir) - return - if(zoomed) - zoom(user, FALSE) //Moved while scope was booting, so we unzoom - -/obj/item/gun/energy/lwap/on_mob_move(dir, mob/user) - if(scope_active) - to_chat(user, "[src]'s scope is overloaded by movement and shuts down!") - zoom(user, FALSE) + AddComponent(/datum/component/scope, range_modifier = 2, time_to_scope = 3 SECONDS, movement_cancels_scope = TRUE) + +/obj/item/gun/energy/lwap/on_scope_success(mob/living/user) + to_chat(user, "SCOPE_CREEPER_[rand(1, 9999)] Online.") + select_fire(user) + user.apply_status_effect(STATUS_EFFECT_LWAPSCOPE) + +/obj/item/gun/energy/lwap/on_scope_end(mob/living/user) + select_fire(user) + user.remove_status_effect(STATUS_EFFECT_LWAPSCOPE) /obj/item/gun/energy/lwap/attack_self() return //no manual ammo changing. @@ -273,7 +255,7 @@ return /// Special version given by the Safety Override upgrade and as a standard module for ERT engiborgs. -/obj/item/gun/energy/emitter/cyborg/proto +/obj/item/gun/energy/emitter/cyborg/proto name = "mobile proto-emitter" desc = "An emitter removed from its base and attached to a laser cannon frame. This one operates on unoptimised software, reducing its effectiveness." ammo_type = list(/obj/item/ammo_casing/energy/emitter/cyborg/proto) diff --git a/code/modules/projectiles/guns/projectile/sniper.dm b/code/modules/projectiles/guns/projectile/sniper.dm index 43781e87570a..f8d317464e03 100644 --- a/code/modules/projectiles/guns/projectile/sniper.dm +++ b/code/modules/projectiles/guns/projectile/sniper.dm @@ -15,14 +15,18 @@ can_unsuppress = TRUE can_suppress = TRUE w_class = WEIGHT_CLASS_NORMAL - zoomable = TRUE - zoom_amt = 7 //Long range, enough to see in front of you, but no tiles behind you. slot_flags = SLOT_FLAG_BACK actions_types = list() execution_speed = 8 SECONDS + var/zoomable = TRUE + +/obj/item/gun/projectile/automatic/sniper_rifle/Initialize(mapload) + . = ..() + if(zoomable) + AddComponent(/datum/component/scope, range_modifier = 2) /obj/item/gun/projectile/automatic/sniper_rifle/process_fire(atom/target, mob/living/user, message = TRUE, params, zone_override, bonus_spread = 0) - if(istype(chambered.BB, /obj/item/projectile/bullet/sniper) && !zoomed) + if(istype(chambered.BB, /obj/item/projectile/bullet/sniper) && !HAS_TRAIT(user, TRAIT_SCOPED)) var/obj/item/projectile/bullet/sniper/S = chambered.BB if(S.non_zoom_spread) to_chat(user, "[src] must be zoomed in to fire this ammunition accurately!") diff --git a/code/modules/projectiles/guns/rocket.dm b/code/modules/projectiles/guns/rocket.dm index 1a6755a59e24..1e3f111479ce 100644 --- a/code/modules/projectiles/guns/rocket.dm +++ b/code/modules/projectiles/guns/rocket.dm @@ -13,11 +13,14 @@ fire_sound = 'sound/weapons/blastcannon.ogg' fire_delay = 40 recoil = 2 - zoomable = TRUE - zoom_amt = 7 var/missile_speed = 2 var/missile_range = 30 + +/obj/item/gun/rocketlauncher/Initialize(mapload) + . = ..() + AddComponent(/datum/component/scope, range_modifier = 2) + /obj/item/gun/rocketlauncher/examine(mob/user) . = ..() . += "It is currently [chambered ? "" : "un"]loaded." diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 476fa574fcf7..ff3c7b741b4f 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -502,6 +502,17 @@ build_path = /obj/item/organ/internal/eyes/cybernetic/thermals category = list("Medical") +/datum/design/cyberimp_scope + name = "Kaleido Optics Implant" + desc = "These cybernetic eye implants will let you zoom in on far away objects. Many users find it disorienting, and find it hard to interact with things near them when active." + id = "ci-scope" + req_tech = list("materials" = 6, "programming" = 4, "biotech" = 7, "magnets" = 5,"plasmatech" = 4) + build_type = PROTOLATHE | MECHFAB + construction_time = 60 + materials = list(MAT_METAL = 600, MAT_GLASS = 600, MAT_SILVER = 600, MAT_GOLD = 600, MAT_PLASMA = 1000, MAT_DIAMOND = 2000) + build_path = /obj/item/organ/internal/eyes/cybernetic/scope + category = list("Medical") + /datum/design/cyberimp_antidrop name = "Anti-Drop Implant" desc = "This cybernetic brain implant will allow you to force your hand muscles to contract, preventing item dropping. Twitch ear to toggle." diff --git a/code/modules/ruins/ghost_bar.dm b/code/modules/ruins/ghost_bar.dm index f2061ec251a8..d212ddb88b7b 100644 --- a/code/modules/ruins/ghost_bar.dm +++ b/code/modules/ruins/ghost_bar.dm @@ -1,3 +1,5 @@ +GLOBAL_LIST_EMPTY(occupants_by_key) + /obj/effect/mob_spawn/human/alive/ghost_bar name = "ghastly rejuvenator" mob_name = "ghost bar occupant" @@ -83,6 +85,11 @@ implant.insert(H) log_game("[ckey] has entered the ghost bar") playsound(src, 'sound/machines/wooden_closet_open.ogg', 50) + var/mob/old_mob = GLOB.occupants_by_key["[H.ckey]"] + if(old_mob) + qdel(old_mob) + GLOB.occupants_by_key["[H.ckey]"] = H + RegisterSignal(H, COMSIG_PARENT_QDELETING, PROC_REF(clear_references_to_owner)) /obj/effect/mob_spawn/human/alive/ghost_bar/proc/equip_item(mob/living/carbon/human/H, path, slot) var/obj/item/I = new path(H) @@ -90,6 +97,10 @@ H.speaks_ooc = TRUE return I +/obj/effect/mob_spawn/human/alive/ghost_bar/proc/clear_references_to_owner(mob/mob_to_obliterate) + SIGNAL_HANDLER // COMSIG_PARENT_QDELETING + GLOB.occupants_by_key -= mob_to_obliterate.ckey + /obj/structure/ghost_bar_cryopod name = "returning sarcophagus" desc = "Returns you back to the world of the dead." diff --git a/code/modules/surgery/organs/autosurgeon.dm b/code/modules/surgery/organs/autosurgeon.dm index 69678826def5..427b07d51f38 100644 --- a/code/modules/surgery/organs/autosurgeon.dm +++ b/code/modules/surgery/organs/autosurgeon.dm @@ -141,6 +141,9 @@ /obj/item/autosurgeon/organ/syndicate/xray_eyes starting_organ = /obj/item/organ/internal/eyes/cybernetic/xray/hardened +/obj/item/autosurgeon/organ/syndicate/scope_eyes + starting_organ = /obj/item/organ/internal/eyes/cybernetic/scope/hardened + /obj/item/autosurgeon/organ/syndicate/anti_stam starting_organ = /obj/item/organ/internal/cyberimp/brain/anti_stam/hardened diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index 373303286c60..a7b13fe9aff9 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -167,6 +167,38 @@ emp_proof = TRUE origin_tech = "materials=6;programming=5;biotech=6;magnets=6;syndicate=3" +/obj/item/organ/internal/eyes/cybernetic/scope + name = "\improper Kaleido Optics eyes" + desc = "These cybernetic eye implants will let you zoom in on far away objects. Many users find it disorienting, and find it hard to interact with things near them when active." + eye_color = "#6f00ff" + flash_protect = FLASH_PROTECTION_EXTRA_SENSITIVE + origin_tech = "materials=5;programming=4;biotech=4;magnets=4" + var/scope_range = 0.8 //Only used in initialize. Greatly nerfed zoom range, since you are not taking the time zoom delay the lwap has. + var/active = FALSE + +/obj/item/organ/internal/eyes/cybernetic/scope/Initialize(mapload) + . = ..() + AddComponent(/datum/component/scope, range_modifier = scope_range, item_action_type = /datum/action/item_action/organ_action/toggle, allow_middle_click = TRUE) + +/obj/item/organ/internal/eyes/cybernetic/scope/insert(mob/living/carbon/human/M, special) + . = ..() + flash_protect = FLASH_PROTECTION_NONE //Resets it to none, so we can just flip to inital each time it is used. + +/obj/item/organ/internal/eyes/cybernetic/scope/ui_action_click(mob/user, actiontype) + active = !active + if(active) + flash_protect = initial(flash_protect) + else + flash_protect = FLASH_PROTECTION_NONE + +/obj/item/organ/internal/eyes/cybernetic/scope/hardened + name = "\improper Hardened Kaleido Optics eyes" + desc = "These cybernetic eye implants will let you zoom in on far away objects. Many users find it disorienting, and find it hard to interact with things near them when active. This pair has been hardened for special operations personnel, and has enhanced zoom functionality." + flash_protect = FLASH_PROTECTION_SENSITIVE + origin_tech = "materials=6;programming=5;biotech=6;magnets=6;syndicate=3" + scope_range = 1.25 + emp_proof = TRUE + /obj/item/organ/internal/eyes/cybernetic/flashlight name = "flashlight eyes" desc = "It's two flashlights rigged together with some wire. Why would you put these in someone's head?" diff --git a/html/statbrowser.css b/html/statbrowser.css index 11eda40d03d5..d78fe76184ab 100644 --- a/html/statbrowser.css +++ b/html/statbrowser.css @@ -158,19 +158,24 @@ td { pointer-events: none; } -.link { - display: inline; +.link, +.listedturf_link { background: none; border: none; - padding: 7px 14px; color: black; text-decoration: none; cursor: pointer; font-size: 13px; +} + +.link { + display: inline; + padding: 7px 14px; margin: 2px 2px; } -.link:hover { +.link:hover, +.listedturf_link:hover { text-decoration: underline; } @@ -225,7 +230,8 @@ body.dark { color: #b2c4dd; } -.dark .link { +.dark .link, +.dark .listedturf_link{ color: #abc6ec; } @@ -268,7 +274,8 @@ body.ntos { color: #b2c4dd; } -.ntos .link { +.ntos .link, +.ntos .listedturf_link{ color: #abc6ec; } @@ -311,7 +318,8 @@ body.paradise { color: #dec5bd; } -.paradise .link { +.paradise .link, +.paradise .listedturf_link{ color: #edc1b2; } @@ -354,6 +362,7 @@ body.syndicate { color: #debdbd; } -.syndicate .link { +.syndicate .link, +.syndicate .listedturf_link { color: #edb2b2; } diff --git a/html/statbrowser.js b/html/statbrowser.js index 7fa1a1f8bcc2..c44d424a1b36 100644 --- a/html/statbrowser.js +++ b/html/statbrowser.js @@ -14,6 +14,13 @@ if (!String.prototype.trim) { }; } +// For sending BYOND debug logs ----------------------------------------------- +// If you use this, you'll need to uncomment the Statpanel-Debug message +// handling, currently in code/modules/client/client_procs.dm +function log_debug(data) { + Byond.sendMessage("Statpanel-Debug", JSON.stringify(data)); +} + // Status panel implementation ------------------------------------------------ var status_tab_parts = [["Loading...", ""]]; var current_tab = null; @@ -22,14 +29,20 @@ var href_token = null; var verb_tabs = []; var verbs = [["", ""]]; // list with a list inside var permanent_tabs = []; // tabs that won't be cleared by wipes -var turfcontents = []; +var turf_row_inner_height = 33; +var turf_row_outer_height = 35; +var turf_rows = {}; +var turf_incomplete_rows = {}; +var turf_size = 0; +var turf_image_errors = {}; +var turfcontents = {"total": 0}; var turfname = ""; +var imageFirstRetryDelay = 50; var imageRetryDelay = 500; var imageRetryLimit = 50; var menu = document.getElementById('menu'); var under_menu = document.getElementById('under_menu'); var statcontentdiv = document.getElementById('statcontent'); -var storedimages = []; var split_admin_tabs = false; // Any BYOND commands that could result in the client's focus changing go through this @@ -371,79 +384,217 @@ function draw_mc() { document.getElementById("statcontent").appendChild(table); } -function iconError(e) { - if(current_tab != turfname) { - return; +function listedturf_add_row(table, table_index, true_index) { + let row = table.insertRow(table_index); + row.style.height = turf_row_inner_height + "px" + row.style.padding = "0px" + row.style.margin = "0px" + turf_rows[true_index] = row; + turf_incomplete_rows[true_index] = true_index + 1; +} + +function listedturf_fill_row(row, item_index) { + let object_info = turfcontents["" + item_index]; + if(!object_info) { + return false; } - setTimeout(function () { - var node = e.target; - var current_attempts = Number(node.getAttribute("data-attempts")) || 0 - if (current_attempts > imageRetryLimit) { - return; + + let cell = document.createElement("td"); + cell.style.height = turf_row_inner_height + "px" + cell.style.padding = "0px" + cell.style.margin = "0px" + row.appendChild(cell) + + var button = document.createElement("div"); + button.className = "listedturf_link"; + var clickcatcher = ""; + button.onmousedown = function (object_info) { + // The outer function is used to close over a fresh "object_info" + // variable, rather than every onmousedown getting the "object_info" + // of the last entry. + return function (e) { + e.preventDefault(); + clickcatcher = "?src=" + object_info[1]; + switch (e.button) { + case 1: + clickcatcher += ";statpanel_item_click=middle" + break; + case 2: + clickcatcher += ";statpanel_item_click=right" + break; + default: + clickcatcher += ";statpanel_item_click=left" + } + if (e.shiftKey) { + clickcatcher += ";statpanel_item_shiftclick=1"; + } + if (e.ctrlKey) { + clickcatcher += ";statpanel_item_ctrlclick=1"; + } + if (e.altKey) { + clickcatcher += ";statpanel_item_altclick=1"; + } + window.location.href = clickcatcher; } - var src = node.src; - node.src = null; - node.src = src + '#' + current_attempts; - node.setAttribute("data-attempts", current_attempts + 1) - draw_listedturf(); - }, imageRetryDelay); + }(object_info); + cell.appendChild(button); + + let img = document.createElement("img"); + img.id = object_info[1]; + img.src = object_info[2]; + img.style.verticalAlign = "middle"; + img.onerror = function (object_info) { + return function () { + let delay = imageRetryDelay; + if (!turf_image_errors[object_info[3]]) { + turf_image_errors[object_info[3]] = 0; + delay = imageFirstRetryDelay; + } + turf_image_errors[object_info[3]]++; + if (turf_image_errors[object_info[3]] > imageRetryLimit) { + return; + } + + Byond.sendMessage("Resend-Asset", object_info[3]); + setTimeout(function () { + // Use the failure count as a cachebreaker to force-reload. + let img = document.getElementById(object_info[1]); + img.src = object_info[2] + "?" + turf_image_errors[object_info[3]]; + }, imageRetryDelay); + } + }(object_info); + button.appendChild(img); + + var label = document.createElement("span"); + label.style.marginLeft = "5px"; + label.textContent = object_info[0]; + button.appendChild(label); + + return true; } -function draw_listedturf() { - statcontentdiv.textContent = ""; - var table = document.createElement("table"); - for (var i = 0; i < turfcontents.length; i++) { - var part = turfcontents[i]; - if (storedimages[part[1]] == null && part[2]) { - var img = document.createElement("img"); - img.src = part[2]; - img.id = part[1]; - storedimages[part[1]] = part[2]; - img.onerror = iconError; - table.appendChild(img); - } else { - var img = document.createElement("img"); - img.onerror = iconError; - img.src = storedimages[part[1]]; - img.id = part[1]; - table.appendChild(img); +function listedturf_fill_all() { + for(let i in turf_incomplete_rows) { + let item_index = turf_incomplete_rows[i]; + if(!turf_rows[i] || listedturf_fill_row(turf_rows[i], item_index)) { + delete turf_incomplete_rows[i]; } - var b = document.createElement("div"); - var clickcatcher = ""; - b.className = "link"; - b.onmousedown = function (part) { - // The outer function is used to close over a fresh "part" variable, - // rather than every onmousedown getting the "part" of the last entry. - return function (e) { - e.preventDefault(); - clickcatcher = "?src=" + part[1]; - switch (e.button) { - case 1: - clickcatcher += ";statpanel_item_click=middle" - break; - case 2: - clickcatcher += ";statpanel_item_click=right" - break; - default: - clickcatcher += ";statpanel_item_click=left" - } - if (e.shiftKey) { - clickcatcher += ";statpanel_item_shiftclick=1"; - } - if (e.ctrlKey) { - clickcatcher += ";statpanel_item_ctrlclick=1"; - } - if (e.altKey) { - clickcatcher += ";statpanel_item_altclick=1"; - } - window.location.href = clickcatcher; + } +} + +var suppress_next_scroll_message = false; +/* We keep a sliding "window" of listedturf items loded. On scroll, we add and + * remove table rows to maintain that window, and update the size of the + * padding row at the top of the table to keep them in the right spot. + */ +function listedturf_scrolled() { + let top_edge = document.documentElement.scrollTop; + let height = document.documentElement.clientHeight; + let bottom_edge = top_edge + height; + let total = document.documentElement.scrollHeight; + let table = document.getElementById("listedturf_table"); + let padding = document.getElementById("listedturf_padding"); + + if (!turf_rows.initialized) { + turf_rows = { + initialized: true, + min_row: 0, + max_row: 0, + }; + } + + if (turf_size === 0) { + return; + } + + let desired_min_row = Math.min(turf_size, Math.max(0, Math.floor(top_edge / turf_row_outer_height) - 10)); + let desired_max_row = Math.min(turf_size, desired_min_row + Math.ceil(height / turf_row_outer_height) + 21); + padding.style.height = (desired_min_row * turf_row_outer_height) + "px"; + if(desired_min_row == turf_rows.min_row && desired_max_row == turf_rows.max_row) { + listedturf_fill_all(); + suppress_next_scroll_message = false; + return; + } + + if (desired_min_row < turf_rows.min_row) { + for (let i = desired_min_row; i < turf_rows.min_row; i++) { + listedturf_add_row(table, i - desired_min_row + 1, i); + } + } else if (desired_min_row > turf_rows.min_row) { + for (let i = turf_rows.min_row; i < desired_min_row && i < turf_rows.max_row; i++) { + if(turf_rows[i]) { + turf_rows[i].remove(); + delete turf_rows[i]; } - }(part); - b.textContent = part[0]; - table.appendChild(b); - table.appendChild(document.createElement("br")); + } } - document.getElementById("statcontent").appendChild(table); + turf_rows.min_row = desired_min_row; + + padding.style.height = turf_rows.min_row * turf_row_outer_height + "px" + + + if (desired_max_row < turf_rows.max_row) { + for (let i = Math.max(desired_max_row, turf_rows.min_row); i < turf_rows.max_row; i++) { + if(turf_rows[i]) { + turf_rows[i].remove(); + delete turf_rows[i]; + } + } + } else if (desired_max_row > turf_rows.max_row) { + for (let i = Math.max(turf_rows.min_row, turf_rows.max_row); i < desired_max_row; i++) { + listedturf_add_row(table, i - turf_rows.min_row + 1, i); + } + } + turf_rows.max_row = desired_max_row; + + listedturf_fill_all(); + + if (!suppress_next_scroll_message) { + Byond.sendMessage("Listedturf-Scroll", {"min": turf_rows.min_row, "max": turf_rows.max_row}) + } + suppress_next_scroll_message = false; +} + +function draw_listedturf() { + if(document.getElementById("listedturf_div")) { + let div = document.getElementById("listedturf_div"); + div.style.height = (turf_row_outer_height * turf_size) + "px"; + suppress_next_scroll_message = true; + listedturf_scrolled(); + return + } + + statcontentdiv.textContent = ""; + turf_rows = {}; + window.onscroll = function() { listedturf_scrolled(); }; + + let div = document.createElement("div"); + div.id = "listedturf_div"; + div.style.height = (turf_row_outer_height * turf_size) + "px"; + document.getElementById("statcontent").appendChild(div); + + let table = document.createElement("table"); + table.id = "listedturf_table"; + table.style.width = "100%" + table.style.height = "100%" + div.appendChild(table) + + let padding = document.createElement("tr"); + padding.id = "listedturf_padding"; + padding.style.height = "0px"; + padding.style.padding = "0px" + padding.style.margin = "0px" + table.appendChild(padding); + + let end_flex = document.createElement("tr"); + end_flex.id = "listedturf_end_flex"; + end_flex.style.height = "100%"; + end_flex.style.padding = "0px" + end_flex.style.margin = "0px" + table.appendChild(end_flex); + + suppress_next_scroll_message = true; + listedturf_scrolled(); } function remove_listedturf() { @@ -452,6 +603,14 @@ function remove_listedturf() { if (current_tab == turfname) { tab_change("Status"); } + if(document.getElementById("listedturf_div")) { + document.getElementById("listedturf_div").remove(); + } + turf_rows = {}; + turf_incomplete_rows = {}; + turf_size = 0; + turfcontents = {"total": 0}; + turfname = ""; } function remove_mc() { @@ -738,6 +897,7 @@ Byond.subscribeTo('remove_mc_tab', function (removeHref) { Byond.subscribeTo('update_listedturf', function (TC) { turfcontents = TC; + turf_size = TC["total"]; if (current_tab == turfname) { draw_listedturf(); } diff --git a/icons/_nanomaps/Delta_nanomap_z1.png b/icons/_nanomaps/Delta_nanomap_z1.png index dc2d2d09aa3c..5c13b4e17611 100644 Binary files a/icons/_nanomaps/Delta_nanomap_z1.png and b/icons/_nanomaps/Delta_nanomap_z1.png differ diff --git a/icons/effects/mob_hunt.dmi b/icons/effects/mob_hunt.dmi deleted file mode 100644 index 1eea83a9afa4..000000000000 Binary files a/icons/effects/mob_hunt.dmi and /dev/null differ diff --git a/icons/mob/screen_scope.dmi b/icons/mob/screen_scope.dmi new file mode 100644 index 000000000000..03ca5665dbfe Binary files /dev/null and b/icons/mob/screen_scope.dmi differ diff --git a/icons/obj/computer.dmi b/icons/obj/computer.dmi index 5b56dd841d30..46b03c242261 100644 Binary files a/icons/obj/computer.dmi and b/icons/obj/computer.dmi differ diff --git a/interface/skin.dmf b/interface/skin.dmf index 729792edc29f..df48d1dcdd7c 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -21,6 +21,11 @@ menu "menu" command = "SS-Info-Breakdown" category = "&File" is-disabled = true + elem "profilecode" + name = "&Profile Code" + command = ".profile" + category = "&File" + is-disabled = true elem name = "&Quit" command = ".quit" diff --git a/paradise.dme b/paradise.dme index 427bca5bcd27..90aa09969753 100644 --- a/paradise.dme +++ b/paradise.dme @@ -90,7 +90,6 @@ #include "code\__DEFINES\mod.dm" #include "code\__DEFINES\move_force.dm" #include "code\__DEFINES\muzzle_flash.dm" -#include "code\__DEFINES\nanomob_defines.dm" #include "code\__DEFINES\newscaster_defines.dm" #include "code\__DEFINES\particle_defines.dm" #include "code\__DEFINES\pda.dm" @@ -135,6 +134,7 @@ #include "code\__DEFINES\vv.dm" #include "code\__DEFINES\wires_defines.dm" #include "code\__DEFINES\zlevel_defines.dm" +#include "code\__DEFINES\zoom.dm" #include "code\__DEFINES\dcs\dcs_flags.dm" #include "code\__DEFINES\dcs\dcs_helpers.dm" #include "code\__DEFINES\dcs\signals.dm" @@ -294,7 +294,6 @@ #include "code\controllers\subsystem\SSmachinery.dm" #include "code\controllers\subsystem\SSmetrics.dm" #include "code\controllers\subsystem\SSmobs.dm" -#include "code\controllers\subsystem\SSnano_mob_hunter.dm" #include "code\controllers\subsystem\SSnightshift.dm" #include "code\controllers\subsystem\SSnpcpool.dm" #include "code\controllers\subsystem\SSoverlays.dm" @@ -420,6 +419,7 @@ #include "code\datums\components\persistent_overlay.dm" #include "code\datums\components\proximity_monitor.dm" #include "code\datums\components\radioactive.dm" +#include "code\datums\components\scope.dm" #include "code\datums\components\shielded.dm" #include "code\datums\components\slippery.dm" #include "code\datums\components\spawner.dm" @@ -1491,11 +1491,6 @@ #include "code\modules\arcade\claw_game.dm" #include "code\modules\arcade\prize_counter.dm" #include "code\modules\arcade\prize_datums.dm" -#include "code\modules\arcade\mob_hunt\battle_computer.dm" -#include "code\modules\arcade\mob_hunt\mob_avatar.dm" -#include "code\modules\arcade\mob_hunt\mob_cards.dm" -#include "code\modules\arcade\mob_hunt\mob_datums.dm" -#include "code\modules\arcade\mob_hunt\mob_type_datums.dm" #include "code\modules\assembly\assebmly_holder.dm" #include "code\modules\assembly\assembly.dm" #include "code\modules\assembly\assembly_helpers.dm" @@ -1521,7 +1516,6 @@ #include "code\modules\asset_cache\assets\asset_emoji.dm" #include "code\modules\asset_cache\assets\asset_jquery.dm" #include "code\modules\asset_cache\assets\asset_materials.dm" -#include "code\modules\asset_cache\assets\asset_mob_hunt.dm" #include "code\modules\asset_cache\assets\asset_nanomap.dm" #include "code\modules\asset_cache\assets\asset_orbit_icons.dm" #include "code\modules\asset_cache\assets\asset_panels.dm" @@ -2496,7 +2490,6 @@ #include "code\modules\pda\core_apps.dm" #include "code\modules\pda\messenger.dm" #include "code\modules\pda\messenger_plugins.dm" -#include "code\modules\pda\mob_hunt_game_app.dm" #include "code\modules\pda\nanobank.dm" #include "code\modules\pda\PDA.dm" #include "code\modules\pda\pda_tgui.dm" diff --git a/sound/AI/traderdeny.ogg b/sound/AI/traderdeny.ogg new file mode 100644 index 000000000000..b4d215468a77 Binary files /dev/null and b/sound/AI/traderdeny.ogg differ diff --git a/sound/AI/tradergranted.ogg b/sound/AI/tradergranted.ogg new file mode 100644 index 000000000000..4c8d8791ff46 Binary files /dev/null and b/sound/AI/tradergranted.ogg differ diff --git a/sound/weapons/scope.ogg b/sound/weapons/scope.ogg new file mode 100644 index 000000000000..37d70b6ff4c3 Binary files /dev/null and b/sound/weapons/scope.ogg differ diff --git a/tgui/packages/tgui/interfaces/CommunicationsComputer.js b/tgui/packages/tgui/interfaces/CommunicationsComputer.js index 4cb4eacabaff..9563fdc31366 100644 --- a/tgui/packages/tgui/interfaces/CommunicationsComputer.js +++ b/tgui/packages/tgui/interfaces/CommunicationsComputer.js @@ -365,14 +365,6 @@ const PlayerPage = (props, context) => { onClick={() => act('messagelist')} /> - -