Merge branch 'master' into protean-rework

This commit is contained in:
BlackMajor
2023-02-23 14:31:09 +13:00
310 changed files with 144992 additions and 152112 deletions
@@ -0,0 +1,25 @@
/obj/item/weapon/deadringer/process()
if(activated)
if (ismob(src.loc))
var/mob/living/carbon/human/H = src.loc
watchowner = H
if(H.getBruteLoss() > bruteloss_prev || H.getFireLoss() > fireloss_prev)
deathprevent()
activated = 0
if(watchowner.isSynthetic())
to_chat(watchowner, "<font color='blue'>You fade into nothingness! [src]'s screen blinks, being unable to copy your synthetic body!</font>")
else
to_chat(watchowner, "<font color='blue'>You fade into nothingness, leaving behind a fake body!</font>")
icon_state = "deadringer_cd"
timer = 5
return
if(timer > 0)
timer--
if(timer == 2)
reveal()
if(corpse)
new /obj/effect/effect/smoke/chem(corpse.loc)
qdel(corpse)
if(timer == 0)
icon_state = "deadringer"
return
@@ -0,0 +1,6 @@
/obj/item/clothing/shoes/apply_blood(var/image/standing)
if(blood_DNA && blood_sprite_state && ishuman(loc))
var/mob/living/carbon/human/H = loc
var/image/bloodsies = image(icon = H.digitigrade ? 'modular_chomp/icons/mob/human_races/masks/blood_digitigrade.dmi' : H.species.get_blood_mask(H), icon_state = blood_sprite_state)
bloodsies.color = blood_color
standing.add_overlay(bloodsies)
@@ -0,0 +1,71 @@
/obj/item/weapon/rig/ch/precursor
name = "xenotech hardsuit control module"
desc = "A purple hardsuit gleaming with energy and alien metals."
suit_type = "precursor hardsuit"
icon = 'modular_chomp/icons/obj/rig_modules_ch.dmi'
icon_state = "precursor_rig"
armor = list(melee = 50, bullet = 50, laser = 70, energy = 70, bomb = 60, bio = 100, rad = 80)
var/block_chance = 15
slowdown = 0
chest_type = /obj/item/clothing/suit/space/rig/ch/precursor
helm_type = /obj/item/clothing/head/helmet/space/rig/ch/precursor
glove_type = /obj/item/clothing/gloves/gauntlets/rig/ch/precursor
boot_type = /obj/item/clothing/shoes/magboots/rig/ch/precursor
req_access = list()
req_one_access = list()
/obj/item/clothing/suit/space/rig/ch/precursor
name = "protective vest"
icon = 'icons/obj/clothing/spacesuits_ch.dmi'
desc = "Light weight but oddly protective plating."
var/block_chance = 15
/obj/item/clothing/head/helmet/space/rig/ch/precursor
name = "helmet"
icon = 'icons/obj/clothing/hats_ch.dmi'
desc = "A protective dome for your head."
var/block_chance = 15
/obj/item/clothing/gloves/gauntlets/rig/ch/precursor
name = "gloves"
icon = 'icons/obj/clothing/gloves_ch.dmi'
desc = "Gloves created with alien tech"
var/block_chance = 15
/obj/item/clothing/shoes/magboots/rig/ch/precursor
name = "boots"
icon = 'icons/obj/clothing/shoes_ch.dmi'
desc = "A pair of grabby boots"
var/block_chance = 15
/obj/item/weapon/rig/ch/precursor/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
if(prob(block_chance))
user.visible_message("<span class='danger'>\The [src] completely absorbs [attack_text]!</span>")
return TRUE
return FALSE
/obj/item/clothing/suit/space/rig/ch/precursor/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
if(prob(block_chance))
user.visible_message("<span class='danger'>\The [src] completely absorbs [attack_text]!</span>")
return TRUE
return FALSE
/obj/item/clothing/head/helmet/space/rig/ch/precursor/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
if(prob(block_chance))
user.visible_message("<span class='danger'>\The [src] completely absorbs [attack_text]!</span>")
return TRUE
return FALSE
/obj/item/clothing/gloves/gauntlets/rig/ch/precursor/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
if(prob(block_chance))
user.visible_message("<span class='danger'>\The [src] completely absorbs [attack_text]!</span>")
return TRUE
return FALSE
/obj/item/clothing/shoes/magboots/rig/ch/precursor/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
if(prob(block_chance))
user.visible_message("<span class='danger'>\The [src] completely absorbs [attack_text]!</span>")
return TRUE
return FALSE
@@ -1,3 +1,4 @@
/obj/item/weapon/rig
var/protean = 0
var/obj/item/weapon/storage/backpack/rig_storage
var/obj/item/weapon/storage/backpack/rig_storage
permeability_coefficient = 0 //Protect the squishies, after all this shit should be waterproof.
@@ -3,3 +3,6 @@
/datum/trait/positive/linguist
custom_only = FALSE
/datum/trait/positive/toxin_gut
custom_only = FALSE
@@ -0,0 +1,52 @@
//This hivebots are meant to be high threats, and a tad more fitting of the alien places they tend to be in. Aka mini-bosses.
/mob/living/simple_mob/mechanical/hivebot/precusor
name = "Precursor Hivebot"
icon = 'modular_chomp/icons/mob/hivebot.dmi'
icon_state = "precursorhive"
icon_living = "precursorhive"
attacktext = list("prodded")
maxHealth = 5 LASERS_TO_KILL // 150 health
health = 5 LASERS_TO_KILL
movement_cooldown = 4
melee_damage_lower = 15
melee_damage_upper = 15
var/obj/item/shield_projector/shields = null
/mob/living/simple_mob/mechanical/hivebot/precusor/Initialize(mapload)
shields = new /obj/item/shield_projector/rectangle/automatic/hivebot_drone(src)
return ..()
/mob/living/simple_mob/mechanical/hivebot/precusor/machinegun
projectiletype = /obj/item/projectile/bullet/hivebot
base_attack_cooldown = 0 // Extremly rapid fire with rather weak bullets.
/mob/living/simple_mob/mechanical/hivebot/precusor/laser
projectiletype = /obj/item/projectile/beam/precursor
projectile_dispersion = 5
projectile_accuracy = -30
/mob/living/simple_mob/mechanical/hivebot/precusor/lobber
projectiletype = /obj/item/projectile/arc/blue_energy/precusor
/obj/item/projectile/arc/blue_energy/precusor
name = "energy missile"
icon_state = "force_missile"
armor_penetration = 10
damage = 50 // Mimics the precusor laser damage and armor peircing with a tad more damage because of the lobbying style.
damage_type = BURN
color = "#A020F0"
/obj/item/shield_projector/rectangle/automatic/hivebot_drone
shield_health = 100
max_shield_health = 100
shield_regen_delay = 5 SECONDS
shield_regen_amount = 20
size_x = 3
size_y = 3
color = "#A020F0"
high_color = "#A020F0"
low_color = "#A020F0"
@@ -0,0 +1,5 @@
//Giving the tank hivebot class a new look,
/mob/living/simple_mob/mechanical/hivebot/tank
icon = 'modular_chomp/icons/mob/hivebot.dmi'
icon_state = "blue"
icon_living = "blue"
@@ -1,8 +1,15 @@
/mob/living/simple_mob/mechanical/mecha/combat/gygax
movement_cooldown = 1 //Because normal Gygaxes are tougher then ths boss version with 0 speed
/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced
movement_cooldown = 0 //Because AADG needs all the help it can get.
/mob/living/simple_mob/mechanical/mecha/combat/gygax/aerostat
desc = "A Vir System Authority automated combat mech with an aged apperance."
ai_holder_type = /datum/ai_holder/simple_mob/intentional/adv_dark_gygax
say_list = /datum/say_list/gygax_aerostat
/datum/say_list/gygax_aerostat
speak = list("ALERT.","Hostile-ile-ile entities dee-twhoooo-wected.","Threat parameterszzzz- szzet.","Bring sub-sub-sub-systems uuuup to combat alert alpha-a-a.")
emote_see = list("beeps menacingly","whirrs threateningly","scans its immediate vicinity")
@@ -0,0 +1,47 @@
/mob/living/simple_mob/animal/passive/dog/stray
name = "dog"
desc = "The most standard dog you have ever seen, it even smells like one."
tt_desc = "Canis lupus familiaris"
//faction = "mexico" //They are from Mexico. //Amusing but this prompts aggression from crew-aligned mobs.
icon = 'modular_chomp/icons/turf/desert_tiles.dmi'
icon_state = "dog"
item_state = "dog"
icon_living = "dog"
icon_rest = "dog"
icon_dead = "dog"
health = 50
maxHealth = 50
mob_size = MOB_SMALL
pass_flags = PASSTABLE
can_pull_size = ITEMSIZE_TINY
can_pull_mobs = MOB_PULL_NONE
layer = MOB_LAYER
density = 1
movement_cooldown = 0.75 //roughly a bit faster than a person
response_help = "pets"
response_disarm = "rolls aside"
response_harm = "stomps"
melee_damage_lower = 5
melee_damage_upper = 7
attacktext = list("nips", "bumps", "scratches")
vore_taste = "wet dog"
min_oxy = 16 //Require atleast 16kPA oxygen
minbodytemp = 223 //Below -50 Degrees Celcius
maxbodytemp = 523 //Above 80 Degrees Celcius
heat_damage_per_tick = 3
cold_damage_per_tick = 3
meat_amount = 7
holder_type = /obj/item/weapon/holder/armadillo
ai_holder_type = /datum/ai_holder/simple_mob/armadillo
speak_emote = list("rumbles", "chirr?", "churr")
say_list_type = /datum/say_list/armadillo
@@ -20,6 +20,13 @@
adminbus_trash = TRUE //You know what, sure whatever. It's not like anyone's gonna be taking this bird on unga trips to be their gamer backpack, which kinda was the main reason for the trash eater restrictions in the first place anyway.
faction = "neutral"
say_list_type = /datum/say_list/swoopie
var/static/list/crew_creatures = list( /mob/living/simple_mob/protean_blob,
/mob/living/simple_mob/slime/promethean)
/mob/living/simple_mob/vore/aggressive/corrupthound/swoopie/IIsAlly(mob/living/L)
. = ..()
if(!.) // Outside the faction and not in friends, are they crew
return L?.type in crew_creatures
/mob/living/simple_mob/vore/aggressive/corrupthound/swoopie/init_vore()
if(!voremob_loaded)
@@ -4,10 +4,11 @@
// preferentially take digitigrade value from owner if available, THEN DNA.
// this allows limbs to be set properly when being printed in the bioprinter without an owner
// this also allows the preview mannequin to update properly because customisation topic calls don't call a DNA check
var/check_digi = istype(src,/obj/item/organ/external/leg) || istype(src,/obj/item/organ/external/foot)
if(owner)
digitigrade = owner.digitigrade && (istype(src,/obj/item/organ/external/leg) || istype(src,/obj/item/organ/external/foot))
digitigrade = check_digi && owner.digitigrade
else if(dna)
digitigrade = dna.digitigrade && (istype(src,/obj/item/organ/external/leg) || istype(src,/obj/item/organ/external/foot))
digitigrade = check_digi && dna.digitigrade
var/gender = "m"
var/skip_forced_icon = skip_robo_icon || (digi_prosthetic && digitigrade)
@@ -56,13 +57,15 @@
if((!istype(src,/obj/item/organ/external/head) && !(force_icon && !skip_forced_icon)) || (model && owner && owner.synth_markings))
for(var/M in markings)
var/datum/sprite_accessory/marking/mark_style = markings[M]["datum"]
var/isdigitype = istype(mark_style,/datum/sprite_accessory/marking/digi)
if(!(digitigrade ^ isdigitype)) //Equivalent to XNOR; this code will only run if either both digitigrade and isdigitype are true, or if both are false.
var/icon/mark_s = new/icon("icon" = mark_style.icon, "icon_state" = "[mark_style.icon_state]-[organ_tag]")
mark_s.Blend(markings[M]["color"], mark_style.color_blend_mode) // VOREStation edit
add_overlay(mark_s) //So when it's not on your body, it has icons
mob_icon.Blend(mark_s, ICON_OVERLAY) //So when it's on your body, it has icons
icon_cache_key += "[M][markings[M]["color"]]"
var/isdigitype = mark_style.digitigrade_acceptance
if(check_digi)
if (!(isdigitype & (digitigrade ? MARKING_DIGITIGRADE_ONLY : MARKING_NONDIGI_ONLY))) //checks flags based on which digitigrade type the limb is
continue
var/icon/mark_s = new/icon("icon" = digitigrade ? mark_style.digitigrade_icon : mark_style.icon, "icon_state" = "[mark_style.icon_state]-[organ_tag]")
mark_s.Blend(markings[M]["color"], mark_style.color_blend_mode) // VOREStation edit
add_overlay(mark_s) //So when it's not on your body, it has icons
mob_icon.Blend(mark_s, ICON_OVERLAY) //So when it's on your body, it has icons
icon_cache_key += "[M][markings[M]["color"]]"
if(body_hair && islist(h_col) && h_col.len >= 3)
var/cache_key = "[body_hair]-[icon_name]-[h_col[1]][h_col[2]][h_col[3]]"
if(!limb_icon_cache[cache_key])
@@ -0,0 +1,358 @@
// This is an attempt to make space POI's which load/unload on demand, so we can have 50 overmap POI's without
// 50 z-levels (which may lead to OOM). No idea what forces I'm working with, so let's see how poorly this goes.
// Map templates for this system are stored in space_pois.dm.
GLOBAL_VAR_INIT(dynamic_sector_master, null)
// Also adjust find_z_levels() if you adjust increase dynamic levels, that part is hard-coded so you don't gloss over world.increment_max_z().
#define MAX_DYNAMIC_LEVELS 3
#define MAX_DYNAMIC_POI_DIMENSIONS 200 // Keep this an even number if using even world.maxx/maxy. This value MUST
// a large enough static border to fit OM ships. A 30x30 ship means max dimensions must be 60 less than the z-level max
// to keep a 30 turf border on each side of the load/unload area.
// The "master" object, which creates + stores + registers the actual z-levels used for dynamic POI's. Handles overmap
// sanity checks, + procs, should not be directly accessible by players. Do not use in maps. Only 1 should exist currently.
/obj/effect/overmap/visitable/dynamic
name = "DO NOT USE IN MAPS"
known = FALSE
scannable = FALSE
invisibility = 101
in_space = TRUE
scanner_desc = "You should not see this."
var/generated_z = FALSE
var/base_area = /area/space
// Initialized to match max dynamic levels.
map_z = list(null,null,null)
// Overmap poi's CURRENTLY USING a z-level. Dynamic POI's get referenced here when loading in and removed when unloaded.
// Length must match map_z length or bad things may happen.
var/list/active_pois[MAX_DYNAMIC_LEVELS]
// Reference list of shuttle landmarks for each z-level. Used to register/unregister shuttle landmarks in POI's.
var/list/shuttle_landmarks[MAX_DYNAMIC_LEVELS]
var/list/all_children = list()
// This handles generating and assigning z-levels. Overriding this proc means we don't need a hacky Initialize() override.
// This means extra_z_levels won't do anything, which is intentional.
/obj/effect/overmap/visitable/dynamic/find_z_levels()
if(!generated_z && (GLOB.dynamic_sector_master == src)) // Ensure this only runs once per round.
for(var/i = 1; i <= 3; i++) // Hard-coding limit because this is dangerous.
world.increment_max_z()
map_z[i] = world.maxz
// Spawn shuttle_landmarks near the lower x border, aligned with the POI spawning turf. These move during POI generation.
var/turf/T = locate(10, round(world.maxy/2), map_z[i])
if(istype(T))
var/list/spawn_directions = list() // Stores a landmark for each direction.
for(var/direction in list("FORE", "PORT", "AFT", "STARBOARD"))
var/obj/effect/shuttle_landmark/om_poi/S = new(T, "Subspace sector [i] - [direction]", "dynamic_sector_[i] - [direction]")
spawn_directions[direction] = S
shuttle_landmarks[i] = spawn_directions
generated_z = TRUE
/obj/effect/overmap/visitable/dynamic/Initialize()
if(!GLOB.dynamic_sector_master)
GLOB.dynamic_sector_master = src
. = ..()
if(ispath(base_area))
var/area/spehss = locate(base_area)
if(!istype(spehss))
CRASH("Dynamic POI generation couldn't locate area [base_area].")
base_area = spehss
create_children()
// Create POI objects for each overmap POI template, link to parent. Initialize() of children handles turf assignment
/obj/effect/overmap/visitable/dynamic/proc/create_children()
for(var/datum/map_template/dynamic_overmap/poi as anything in subtypesof(/datum/map_template/dynamic_overmap))
if(!initial(poi.mappath) || !initial(poi.name) || (initial(poi.block_size) > MAX_DYNAMIC_POI_DIMENSIONS)) // Exclude templates without an actual map or are too big (or are not included in the mapping subsystem map_templates)
continue
var/obj/effect/overmap/visitable/dynamic/poi/P = new()
P.my_template = SSmapping.map_templates[initial(poi.name)] // Link to the stored map datums.
P.parent = src
all_children.Add(P)
P.seed_overmap()
// Randomly unload a child POI, if possible. Returns the index of the recovered level if successful, 0 if not.
// Should only be called if active_pois[] is full.
/obj/effect/overmap/visitable/dynamic/proc/cull_child(mob/user)
var/random_index = rand(1,MAX_DYNAMIC_LEVELS)
var/obj/effect/overmap/visitable/dynamic/poi/P
// User selection. Remains random if user cancels.
if(user)
P = tgui_input_list(user, "Would you like to choose which link to sever?", "Sever link", active_pois)
if(P && istype(P) && src.is_empty(P.my_index))
random_index = P.my_index
P.destroy_poi(user)
active_pois[random_index] = null
return random_index
else
return 0
if(is_empty(random_index)) // If this z-level is empty, destroy the poi
P = active_pois[random_index]
P.destroy_poi(user)
active_pois[random_index] = null // Should be set in destroy_poi but this lets us use the z-level again if anything goes horribly wrong.
return random_index
for(var/i = 1, i <= MAX_DYNAMIC_LEVELS, i++) // Level was not empty, check remaining levels
if(i == random_index)
continue // Don't run this one again.
if(is_empty(i))
P = active_pois[i]
P.destroy_poi(user)
active_pois[i] = null
return i
return 0
/obj/effect/overmap/visitable/dynamic/proc/is_empty(var/index, var/mob/observer)
if(!LAZYLEN(map_z))
log_and_message_admins("CANARY: [src] tried to check is_empty, but map_z is `[map_z || "null"]`")
return TRUE
if(!index)
index = 1
testing("Checking if sector at [map_z[index]] has no players.")
for(var/mob/M in global.player_list)
if(M != observer && (M.z == map_z[index]))
testing("There are people on it.")
return FALSE
return TRUE
// You generally shouldn't destroy these.
/obj/effect/overmap/visitable/dynamic/Destroy()
for(var/child in all_children)
qdel(child)
return ..()
// These are the player-viewable/interactible POI's which tell the parent how to use its z-levels. Spawned by the parent.
/obj/effect/overmap/visitable/dynamic/poi
name = "bluespace static"
unknown_name = "bluespace static"
unknown_state = "poi"
known = FALSE
scannable = TRUE
invisibility = 0
in_space = TRUE
map_z = list(null) // Override parent value
active_pois = null
var/obj/effect/overmap/visitable/dynamic/parent // The object which created us.
var/datum/map_template/dynamic_overmap/my_template
var/last_scanned = 0 // Timer to prevent spamming POI loading/unloading since that's probably exploitable.
var/loaded = FALSE
var/my_index = 0 // Tracks which z-level we're using in the parent. Corresponds to index in parent's active_pois[]
/obj/effect/overmap/visitable/dynamic/poi/Initialize()
if(!global.using_map.use_overmap)
return INITIALIZE_HINT_QDEL
// Normally Initialize() would do this but I need it to call after Initialize(), therefore new proc.
/obj/effect/overmap/visitable/dynamic/poi/proc/seed_overmap()
start_x = start_x || rand(OVERMAP_EDGE, global.using_map.overmap_size - OVERMAP_EDGE)
start_y = start_y || rand(OVERMAP_EDGE, global.using_map.overmap_size - OVERMAP_EDGE)
forceMove(locate(start_x, start_y, global.using_map.overmap_z))
for(var/obj/effect/overmap/visitable/dynamic/poi/P in loc.contents) // If we've spawned on another poi, we'll try again once.
if(P == src)
continue
start_x = start_x || rand(OVERMAP_EDGE, global.using_map.overmap_size - OVERMAP_EDGE)
start_y = start_y || rand(OVERMAP_EDGE, global.using_map.overmap_size - OVERMAP_EDGE)
forceMove(locate(start_x, start_y, global.using_map.overmap_z))
break
if(!docking_codes)
docking_codes = "[ascii2text(rand(65,90))][ascii2text(rand(65,90))][ascii2text(rand(65,90))][ascii2text(rand(65,90))]"
if(known)
plane = PLANE_LIGHTING_ABOVE
for(var/obj/machinery/computer/ship/helm/H in global.machines)
H.get_known_sectors()
else
real_appearance = image(icon, src, my_template.poi_icon)
real_appearance.override = TRUE
name = unknown_name
icon_state = unknown_state
color = null
desc = "Scan this to find out more information."
// Grab scanner info from map template, allow the user to load/unload POI's.
/obj/effect/overmap/visitable/dynamic/poi/get_scan_data(user)
scanner_desc = my_template.scanner_desc
if(!known)
known = TRUE
name = my_template.name
icon_state = my_template.poi_icon
color = my_template.poi_color
desc = initial(desc)
if(loaded)
var/confirm = alert(user, "Sever bluespace link? This location will become permanently inaccessible.", "Are you sure?", "No", "Yes")
if(confirm == "Yes")
if(is_empty(1)) // Dynamic POI's should only ever have 1 entry in map_z
destroy_poi(user) // Delete POI from dynamic z-level
else
to_chat(user, "Unable to sever link. Location likely contains living realspace entities.")
else
var/confirm = alert(user, "Transient subspace anomaly detected. Tether object to realspace?", "Stabilize anomaly?", "Yes", "No")
if(confirm == "Yes")
create_poi(user) // Load POI to dynamic z-level
return ..()
// Loads POI template into a dynamic z-level
/obj/effect/overmap/visitable/dynamic/poi/proc/create_poi(mob/user)
if(!parent) // Ideally this should never happen.
log_and_message_admins("Dynamic overmap POI attempted to generate without a parent z-level.")
qdel(src)
return
if(!my_template) // Also shouldn't happen.
log_and_message_admins("Dynamic overmap POI attempted to generate without a map template.")
qdel(src)
return
if(loaded) // These may only be created once.
return
if(last_scanned + 10 SECONDS > world.time)
to_chat(user, "\[REDACTED\] matrix is still recharging!")
return
last_scanned = world.time
for(var/i = 1, i <= MAX_DYNAMIC_LEVELS, i++)
if(!parent.active_pois[i]) // We found an unused z-level
my_index = i
parent.active_pois[i] = src
map_z[1] = parent.map_z[i]
map_sectors["[parent.map_z[i]]"] = src // Pass ownership of z-level to child, probably hacky and terribad, also mandatory for using forceMove() on shuttle landmarks
break // Terminate loop
if(!my_index) // No z-levels available
var/confirm = alert(user, "\[REDACTED\] matrix at capacity; a bluespace link must be permanently severed to stabilize this anomaly. Continue?", "Are you sure?", "No", "Yes")
if(confirm == "Yes")
my_index = parent.cull_child(user)
if(my_index)
parent.active_pois[my_index] = src
map_z[1] = parent.map_z[my_index]
map_sectors["[parent.map_z[my_index]]"] = src
else // Something went wrong, ideally due to all relevant z-levels containing players.
to_chat(user, "Unable to sever any bluespace link. All links likely contain living realspace entities.")
return
else
return
var/turf/T = locate(round(world.maxx/2), round(world.maxy/2), map_z[1]) // Find center turf, or near center for even-dimension maps.
if(!istype(T))
log_debug("Dynamic overmap POI found [T] instead of a valid turf.")
return
// Move the shuttle landmarks.
var/list/landmark_directions = parent.shuttle_landmarks[my_index] // Each shuttle_landmarks entry is an associative list.
var/obj/effect/shuttle_landmark/om_poi/S = landmark_directions["FORE"]
S.forceMove(locate(T.x, min(world.maxy, T.y + round(src.my_template.block_size/2) + rand(10,20)), T.z)) // Above
add_landmark(S) // This lets overmap shuttles find our newly assigned z-level.
S = landmark_directions["AFT"]
S.forceMove(locate(T.x, max(1, T.y - round(src.my_template.block_size/2) - rand(10,20)), T.z))// Below
add_landmark(S)
S = landmark_directions["PORT"]
S.forceMove(locate(max(1, T.x - round(src.my_template.block_size/2) - rand(10,20)), T.y, T.z)) // Left
add_landmark(S)
S = landmark_directions["STARBOARD"]
S.forceMove(locate(min(world.maxx, T.x + round(src.my_template.block_size/2) + rand(10,20)), T.y, T.z)) // Right
add_landmark(S)
my_template.load(T, centered=TRUE)
loaded = TRUE
my_template.update_lighting(T)
to_chat(user, "Stabilization tether successfully created.")
if(my_template.active_icon)
icon_state = my_template.active_icon
/obj/effect/overmap/visitable/dynamic/poi/proc/destroy_poi(mob/user)
if(!loaded) // Ideally this should never happen.
log_debug("Dynamic overmap POI tried to unload itself but is not loaded.")
return
if(!parent) // Also shouldn't happen.
log_and_message_admins("Dynamic overmap POI attempted to unload without a parent z-level.")
loaded = FALSE // Always make sure loaded = FALSE before using qdel(src) in this proc to prevent an infinite loop in Destroy().
qdel(src)
return
if(!((my_index >= 1) && (my_index <= MAX_DYNAMIC_LEVELS))) // Make sure my_index is sane
log_debug("Dynamic overmap POI attempted to unload with an invalid index.")
loaded = FALSE
qdel(src)
return
map_sectors["[parent.map_z[my_index]]"] = parent // Pass ownership back to parent.
parent.active_pois[my_index] = null
if(!LAZYLEN(map_z)) // If this is 0, how did we get this far?
log_and_message_admins("Dynamic overmap POI attempted to unload without a linked z-level.")
loaded = FALSE
qdel(src)
return
to_chat(user, "Destabilization initiated...")
log_debug("Dynamic overmap POI unloading initiated...")
// Some math to return a block of block_size turfs (+ 1 to each dimension to account for even-size maps lacking a true center to safely do math with). Basically, start from center turf and subtract half of block_size for bottom left corner, add for top right corner.
var/list/turfs_to_reset = block(locate(round(world.maxx/2 - my_template.block_size/2 - 1), round(world.maxy/2 - my_template.block_size/2 - 1), map_z[1]), locate(round(world.maxx/2 + my_template.block_size/2 + 1), round(world.maxy/2 + my_template.block_size/2 + 1), map_z[1]))
var/deleted_atoms = 0
if(turfs_to_reset.len)
var/area/A = parent.base_area
if(ispath(A))
A = locate(A)
if(!istype(A))
CRASH("Dynamic POI unloading couldn't locate area [A], unload aborted.")
for(var/turf/T in turfs_to_reset)
for(var/atom/movable/AM in T)
if(istype(AM, /mob/observer))
continue
++deleted_atoms
qdel(AM)
ChangeArea(T, A)
T = T.ChangeTurf(/turf/space)
loaded = FALSE
map_z = null
icon_state = "ring_destroyed"
log_debug("Dynamic POI unload complete, [deleted_atoms] atoms destroyed.")
to_chat(user, "Subspace pocket collapse successful.")
qdel(src)
/obj/effect/overmap/visitable/dynamic/poi/Destroy()
if(loaded)
if(is_empty()) // If we're deleted while loaded but the z-level is occupied, relinquish ownership instead.
destroy_poi()
else
if(parent && (parent.active_pois.len >= my_index) && (parent.map_z.len >= my_index) && (my_index > 0)) // Unless vars are turbofucked.
map_sectors["[parent.map_z[my_index]]"] = parent
parent.active_pois[my_index] = null
if(parent)
parent.all_children.Remove(src)
return ..()
// This assigns map_sectors which we don't want for this subtype since we have other procs to handle it.
/obj/effect/overmap/visitable/dynamic/poi/register_z_levels()
return
// Make sure we reassign our z_level to the parent if one exists and somehow this gets called.
/obj/effect/overmap/visitable/dynamic/poi/unregister_z_levels()
if(parent && LAZYLEN(map_z))
map_sectors["[map_z[1]]"] = parent
map_z = null
return ..()
// Hahah recursion
/obj/effect/overmap/visitable/dynamic/poi/create_children()
return
/obj/effect/overmap/visitable/dynamic/poi/cull_child()
return
/obj/effect/shuttle_landmark/om_poi // Landmarks used in dynamic poi generation
flags = SLANDMARK_FLAG_AUTOSET
base_area = /area/space
base_turf = /turf/space
/obj/effect/shuttle_landmark/om_poi/Initialize(mapload, var/new_name, var/new_tag)
name = new_name
landmark_tag = new_tag
. = ..()
#undef MAX_DYNAMIC_LEVELS
#undef MAX_DYNAMIC_POI_DIMENSIONS
@@ -0,0 +1,81 @@
/* List of player tips
Weighted to emphasize more important over less.area
Weights are not additive. You can have multiple prob(50) items.
prob(50) makes it half as likely to appear and so forth.
When editing the list, please try and keep similar probabilities near each other. High on top, low on bottom */
//argument determines if to pick a random tip or use a forced choice.
/datum/player_tips/proc/pick_tip(var/isSpecific)
var/choice = null
if(!(isSpecific == "none" || isSpecific == "general" || isSpecific == "gameplay" || isSpecific == "roleplay" || isSpecific == "lore" ))
choice = "none" //Making sure that wrong arguments still give tips.
if(isSpecific == "none")
choice = pick (
prob(50); "general",
prob(50); "gameplay",
prob(25); "roleplay",
prob(20); "lore"
)
else
choice = isSpecific
switch(choice)
if("general")
var/info = "The following is a general tip to playing on CHOMPStation! You can disable them using the periodic tips toggle in the Global tab of character setup! \n"
return pick(
prob(60); "[info] Got a question about gameplay, roleplay or the setting? Press F1 to Mentorhelp!",
prob(60); "[info] We have a wiki that is actively updated! Please check it out at https://wiki.chompstation13.net/index.php/Chomp_Station_Wiki for help!",
prob(60); "[info] Unsure about rules? Press F1 and ask our admins for clarification - they are happy to help.",
prob(30); "[info] Don't be afraid to approach your fellow players for advice! Learning things ICly can help build powerful bonds!",
prob(30); "[info] Need some guideance making a character or with roleplay concepts? Our discord's tutoring channel is happy to help!",
prob(30); "[info] Having difficulties getting started? Pressing F3 to speak and typing '; Hello! I'm a new hire. Could someone please give me a tour?' or as appropriate for your character is a good way to start! More help available at: https://wiki.chompstation13.net/index.php/The_Basics",
prob(30); "[info] Want to try out a new department? Consider joining as an intern when it's well-staffed. Our players enjoy teaching eager students. You can approach such roleplay as simply getting taught the local technologies, procedures - you don't need to be 'fresh out of school' to justify it!",
prob(30); "[info] Our discord is an excellent resource to stay up to date about changes and events! If wanting to separate your kink and real identities, Discord has a built in means to swap accounts within the client. It is OK to lurk!",
prob(5); "[info] Got another tip for the list? Please let us know on Discord in the feedback forums!"
)
if("gameplay")
var/info = "The following is a gameplay-focused tip to playing on CHOMPStation! You can disable them using the periodic tips toggle in the Global tab of character setup! \n"
return pick(
prob(50); "[info] To talk to your fellow coworkers, use ';'! You may append it by an exclamation mark, like ';!' to perform an audiable emote. ",
prob(50); "[info] Lost on the map? You can find In-Character help by speaking on the Common Radio. You can do this by pressing F3 and typing ' ; ' before your message. Your fellow co-workers will likely help. If OOC help is preferred, press F1 for mentorhelp. ",
prob(50); "[info] You may set your suit sensors by clicking on the icon in the bottom left corner, then right click the clothes that appear right above it. It is recommended to turn on suit sensors to 'TRACKING' before doing anything dangerous like mining, and to turn them off before digestion scenes as prey.",
prob(35); "[info] It is never a bad idea to visit the medbay if you get injured - small burns and cuts can get infected and become harder to treat! If there is no medical staff, bathrooms and the bar often has a NanoMed on the wall while a medical station can be found on the first deck - with ointments to disinfect cuts and burns, bandages to treat bruises and encourage healing.",
prob(25); "[info] Two control modes exist for SS13 - hotkey ON and hotkey OFF. You can swap between the two modes by pressing TAB. In hotkey mode, to chat you need to press T to say anything which creates a small talking bubble.",
prob(25); "[info] Do you want to shift your character around, for instance to appear as if leaning on the wall? Press CTRL + SHIFT + arrow keys to do so! Moving resets this.",
prob(25); "[info] Emergency Fire Doors seal breaches and keep active fires out. Please do not open them without good reason. SHIFT + CLICK them to get temperature and atmospheric pressure readings.",
prob(25); "[info] The kitchen's Oven can fit multiple ingredients in one slot if you pull the baking tray out first. This is required for most recipes, and the Grille and Deep Frier work the same way!",
prob(10); "[info] Not every hostile NPC you encounter while mining or exploring need to be defeated. Sometimes, it's better to avoid or run away from them.",
prob(1); "[info] Stay robust, my friends.",
prob(35); "[info] You can insert your I.D into your PDA. This frees up your belt from having to carry your PDA. Furthermore, by clicking and dragging the PDA to game screen, you can use it without holding it!",
prob(35); "[info] Your vore-bellies have multiple add-ons! Muffling is excellent to ensure your prey does not accidentally inform everyone about their predicament, and jam suit sensors is a great courtesy to avoid medical being worried about your prey!"
)
if("roleplay")
var/info = "The following is a roleplay-focused tip to playing on CHOMPStation! You can disable them using the periodic tips toggle in the Global tab of character setup! \n"
return pick(
prob(50); "[info] Having difficulty finding scenes? The number one tip that people should take for finding scenes is to be active! Generally speaking, people are more likely to interact with you if you are moving about and doing things. Don't be afraid to talk to people, you're less likely to be approached if you're sat alone at a table silently. People that are looking for scenes generally like to see how you type and RP before they'll start working towards a scene with you.",
prob(50); "[info] Please avoid a character that knows everything. Having only a small set of jobs you are capable of doing can help flesh out your character! It's OK for things to break and fail if nobody is around to fix it - you do not need to do others' jobs.",
prob(25); "[info] Embrace the limits of your character's skillsets! Seeking out other players to help you with a more challenging task might build friendships, or even lead to a scene!",
prob(25); "[info] Slowing down when meeting another player can help with finding roleplay! Your fellow player might be typing up a greeting or an emote, and if you run off you won't see it!",
prob(25); "[info] It is a good idea to wait a few moments after using mechanics like lick, hug or headpat on another player. They might be typing up a response or wish to reciprocate, and if you run away you might miss out!",
prob(25); "[info] Participating in an away mission and see something acting strange? Try emoting or talking to it before resorting to fighting. It may be a GM event!",
prob(15); "[info] We are a medium roleplay server. This does not neccessarily mean 'serious' roleplay, levity and light-hearted RP is more than welcome! Please do not ignore people just because it is unlikely you will be able to scene.",
prob(10); "[info] Sending faxes to central command, using the 'pray' verb or pressing F1 to ahelp are highly encouraged when exploring the gateway or overmap locations! Letting GMs know something fun is happening allows them to spice things up and make the world feel alive!",
prob(40); "[info] Just because you see something doesn't mean your character has to. A courtesy 'missing' of contraband or scene details can go a long way towards preserving everyone's fun!",
prob(25); "[info] It is always a good idea to communicate on your department's private channel (whose key you can learn by examining your headset) when responding to an emergency! This lets your coworkers know if they might be needed!",
prob(25); "[info] While following the SOP is not mandatory, and you are free to break it (albeit, with potential in-character consequences), departments like security and medical do well to be familiar with them! https://wiki.chompstation13.net/index.php/Standard_Operating_Procedure",
prob(25); "[info] Think a player is acting especially antagonistic? It might be better to Ahelp (with F1) rather than try to deal with it icly, staff can make sure it's all okay.",
prob(20); "[info] See a minor infraction as Security with a minimal time punishment? Consider using your ticket printer to give a non obtrusive punishment."
)
if("lore")
var/info = "The following is tip for understanding the lore of CHOMPStation! You can disable them using the periodic tips toggle in the Global tab of character setup! \n"
return pick(
prob(25); "[info] Our lore is somewhat in line with other servers. But not Virgo's. They wrote themselves out of the timeline, lol. The year is 2564 (current year+541).",
prob(75); "[info] You can find a short summary of our setting that everyone should know at https://wiki.chompstation13.net/index.php/Lore",
prob(50); "[info] You are currently working in the Vir system on the NLS Southern Cross telecommunications and traffic control station. https://wiki.chompstation13.net/index.php/Vir",
prob(50); "[info] The majority of employees live at the Northern Star asteroid colony orbiting a gas giant called Kara. It is also a central hub for visitors, logistics, and mining. This is the place the shuttle takes you at the end of the round. You may visit the mines via the exploration shuttles. https://wiki.chompstation13.net/index.php/NCS_Northern_Star",
prob(10); "[info] Thaler is a universal monopoly money. It is backed and supported by Sol Central and its allies. While ubiquitous in frontier worlds, it has an unfavourable exchange rate with most currencies used by well-settled regions, limiting immigration to places such as Earth. https://wiki.chompstation13.net/index.php/Lore"
)
@@ -1,3 +1,19 @@
/datum/design/item/mechfab/rigsuit/phase
name = "hardsuit phase rifle"
desc = "A compact phase rifle for a hardsuit."
id = "rig_gun_phase"
req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 4, TECH_MAGNET = 3, TECH_POWER = 4, TECH_COMBAT = 4)
materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 1250)
build_path = /obj/item/rig_module/mounted/phase
/datum/design/item/mechfab/rigsuit/defib
name = "hardsuit defib unit"
desc = "A rig mounted defib and jumper kit combo."
id = "rig_defib"
req_tech = list(TECH_BIO = 5, TECH_MAGNET = 2, TECH_POWER = 3)
materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 2000)
build_path = /obj/item/rig_module/device/defib
/datum/design/item/mecha/phoron_bore
name = "PB-23 \"Phobos\" Phoron Bore"
category = list("Exosuit Equipment")
@@ -111,18 +127,11 @@
time = 20
materials = list(DEFAULT_WALL_MATERIAL = 15000, MAT_PHORON = 2000, MAT_GOLD = 2000, MAT_VERDANTIUM = 500)
/datum/design/item/mechfab/rigsuit/phase
name = "hardsuit phase rifle"
desc = "A compact phase rifle for a hardsuit."
id = "rig_gun_phase"
req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 4, TECH_MAGNET = 3, TECH_POWER = 4, TECH_COMBAT = 4)
materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 1250)
build_path = /obj/item/rig_module/mounted/phase
/datum/design/item/mechfab/rigsuit/defib
name = "hardsuit defib unit"
desc = "A rig mounted defib and jumper kit combo."
id = "rig_defib"
req_tech = list(TECH_BIO = 5, TECH_MAGNET = 2, TECH_POWER = 3)
materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 2000)
build_path = /obj/item/rig_module/device/defib
/datum/design/item/mechfab/rigsuit/precursor
name = "Xenotech Rig"
desc = "A rig made of alien tech and materials."
id = "rigmodule_precursor"
time = 30
req_tech = list(TECH_MATERIAL = 9, TECH_ENGINEERING = 6, TECH_PHORON = 5, TECH_MAGNET = 6, TECH_POWER = 6, TECH_ILLEGAL = 8, TECH_PRECURSOR = 3)
materials = list(MAT_PLASTEEL = 12000, MAT_GOLD = 5000, MAT_GRAPHITE = 8000, MAT_OSMIUM = 3000, MAT_PLASTIC = 6000, MAT_VERDANTIUM = 7500, MAT_MORPHIUM = 20000)
build_path = /obj/item/weapon/rig/ch/precursor
@@ -6,9 +6,8 @@
// And optionally, this could be gated behind another preference, to prevent stunlock being abused.
if((mob_always_swap || (a_intent == I_HELP || src.restrained()) && (target.a_intent == I_HELP || target.restrained())) && target.canmove && target.handle_micro_bump_helping(src))
return
else if(!(target.a_intent == I_HELP || target.restrained()) && target.handle_micro_bump_other(src))
if(!(target.a_intent == I_HELP || target.restrained()))
if(src.step_mechanics_pref && target.step_mechanics_pref)
target.handle_micro_bump_other(src)
else
target.handle_micro_bump_other(src, 1)
return