diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 8c0309563a..7947aafaea 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -112,6 +112,9 @@ var/protean_drop_whitelist = FALSE + var/rock_climbing = FALSE //If true, allows climbing cliffs using click drag for single Z, walls if multiZ + var/climbing_delay = 1 //If rock_climbing, lower better. + /obj/item/New() ..() if(embed_chance < 0) diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index e7293865e3..57c1d22832 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -17,6 +17,7 @@ var/dirty_prob = 2 // Chance of being dirty roundstart var/dirt = 0 var/special_temperature //Used for turf HE-Pipe interaction + var/climbable = FALSE //Adds proc to wall if set to TRUE on its initialization, defined here since not all walls are subtypes of wall var/icon_edge = 'icons/turf/outdoors_edge.dmi' //VOREStation Addition - Allows for alternative edge icon files @@ -63,6 +64,14 @@ if(istype(loc, /area/chapel)) holy = 1 levelupdate() + if(climbable) + verbs += /turf/simulated/proc/climb_wall + +/turf/simulated/examine(mob/user) + . = ..() + if(climbable) + . += "This [src] looks climbable." + /turf/simulated/proc/AddTracks(var/typepath,var/bloodDNA,var/comingdir,var/goingdir,var/bloodcolor="#A10808") var/obj/effect/decal/cleanable/blood/tracks/tracks = locate(typepath) in src @@ -177,4 +186,4 @@ else if( istype(M, /mob/living/silicon/robot )) new /obj/effect/decal/cleanable/blood/oil(src) else if(ishuman(M)) - add_blood(M) \ No newline at end of file + add_blood(M) diff --git a/code/game/turfs/simulated/outdoors/atmoscaves_vr.dm b/code/game/turfs/simulated/outdoors/atmoscaves_vr.dm index 8ac5dc08e5..7c96f35143 100644 --- a/code/game/turfs/simulated/outdoors/atmoscaves_vr.dm +++ b/code/game/turfs/simulated/outdoors/atmoscaves_vr.dm @@ -2,11 +2,13 @@ oxygen = MOLES_O2STANDARD nitrogen = MOLES_N2STANDARD temperature = T20C + climbable = TRUE /turf/simulated/mineral/ignore_mapgen/cave oxygen = MOLES_O2STANDARD nitrogen = MOLES_N2STANDARD temperature = T20C + climbable = TRUE /turf/simulated/mineral/floor/cave oxygen = MOLES_O2STANDARD diff --git a/code/game/turfs/simulated/wall_types_vr.dm b/code/game/turfs/simulated/wall_types_vr.dm index b4bb8a5c04..b25e6d6f9d 100644 --- a/code/game/turfs/simulated/wall_types_vr.dm +++ b/code/game/turfs/simulated/wall_types_vr.dm @@ -206,6 +206,7 @@ var/list/flesh_overlay_cache = list() /turf/simulated/wall/solidrock icon_state = "solidrock" icon = 'icons/turf/wall_masks_vr.dmi' + climbable = TRUE /turf/simulated/wall/titanium icon_state = "titanium" diff --git a/code/game/turfs/simulated_vr.dm b/code/game/turfs/simulated_vr.dm index 6c7325305f..dc74fd9aba 100644 --- a/code/game/turfs/simulated_vr.dm +++ b/code/game/turfs/simulated_vr.dm @@ -1,5 +1,15 @@ /turf/simulated can_start_dirty = FALSE // We have enough premapped dirt where needed + + /turf/simulated/floor/plating - can_start_dirty = TRUE // But let maints and decrepit areas have some randomness \ No newline at end of file + can_start_dirty = TRUE // But let maints and decrepit areas have some randomness + + +/turf/simulated/proc/toggle_climbability() //Again, b + if(climbable) + verbs -= /turf/simulated/proc/climb_wall + else + verbs += /turf/simulated/proc/climb_wall + climbable = !climbable diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index d347881b40..0b914a6a8c 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -604,7 +604,6 @@ var/water_speed = 0 //Speed boost/decrease in water, lower/negative values mean more speed var/snow_speed = 0 //Speed boost/decrease on snow, lower/negative values mean more speed - var/rock_climbing = FALSE // If true, allows climbing cliffs with clickdrag. var/step_volume_mod = 1 //How quiet or loud footsteps in this shoe are diff --git a/code/modules/clothing/shoes/boots.dm b/code/modules/clothing/shoes/boots.dm index 78949fee1f..69ea46a5f2 100644 --- a/code/modules/clothing/shoes/boots.dm +++ b/code/modules/clothing/shoes/boots.dm @@ -190,6 +190,7 @@ desc = "A pair of winter boots, with metal bracing attached to assist in climbing rocky terrain." icon_state = "climbing_boots" rock_climbing = TRUE + //Climbing delay with boots is 1 /obj/item/clothing/shoes/boots/tactical name = "tactical boots" diff --git a/code/modules/mob/living/carbon/human/species/species_vr.dm b/code/modules/mob/living/carbon/human/species/species_vr.dm index 49962ef351..4199dba6f3 100644 --- a/code/modules/mob/living/carbon/human/species/species_vr.dm +++ b/code/modules/mob/living/carbon/human/species/species_vr.dm @@ -38,6 +38,8 @@ var/micro_size_mod = 0 // How different is our size for interactions that involve us being small? var/macro_size_mod = 0 // How different is our size for interactions that involve us being big? var/digestion_nutrition_modifier = 1 + var/can_climb = FALSE + var/climbing_delay = 1.5 // We climb with a quarter delay /datum/species/proc/give_numbing_bite() //Holy SHIT this is hacky, but it works. Updating a mob's attacks mid game is insane. diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm index 15a5fcabea..67c9ca9322 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm @@ -423,6 +423,8 @@ primitive_form = "Wolpin" color_mult = 1 icon_height = 64 + can_climb = TRUE + climbing_delay = 1 min_age = 18 max_age = 200 @@ -460,4 +462,3 @@ BP_L_FOOT = list("path" = /obj/item/organ/external/foot), BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) ) - diff --git a/code/modules/mob/living/carbon/human/species/station/station_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_vr.dm index 20b5b0bf5d..3dfe6da8e7 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_vr.dm @@ -319,6 +319,8 @@ genders = list(MALE, FEMALE, PLURAL, NEUTER) wikilink="https://wiki.vore-station.net/Tajaran" agility = 90 + can_climb = TRUE + climbing_delay = 1.00 //Cats are good climbers. /datum/species/skrell spawn_flags = SPECIES_CAN_JOIN @@ -387,7 +389,7 @@ tail_animation = 'icons/mob/species/vox/tail.dmi' deform = 'icons/mob/human_races/r_def_vox_old.dmi' color_mult = 1 - + descriptors = list( /datum/mob_descriptor/vox_markings = 0 ) diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm index 2a350374bc..2764e1fce1 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm @@ -218,3 +218,53 @@ cost = 1 var_changes = list("throwforce_absorb_threshold" = 10) + + + +/datum/trait/positive/wall_climber + name = "Climber, Amateur" + desc = "You can climb certain walls without tools! This is likely a personal skill you developed." + tutorial = "You must approach a wall and right click it and select the \ + 'climb wall' verb to climb it. You suffer from a movement delay of 1.5 with this trait.\n \ + Your total climb time is expected to be 17.5 seconds. Tools may reduce this. \n\n \ + This likewise allows descending walls, provided you're facing an empty space and standing on \ + a climbable wall. To climbe like so, use the verb 'Climb Down Wall' in IC tab!" + cost = 1 + custom_only = FALSE + banned_species = list(SPECIES_TAJ, SPECIES_VASILISSAN) // They got unique climbing delay. + var_changes = list("can_climb" = TRUE) + excludes = list(/datum/trait/positive/wall_climber_pro, /datum/trait/positive/wall_climber_natural) + +/datum/trait/positive/wall_climber_natural + name = "Climber, Natural" + desc = "You can climb certain walls without tools! This is likely due to the unique anatomy of your species. CUSTOM AND XENOCHIM ONLY" + tutorial = "You must approach a wall and right click it and select the \ + 'climb wall' verb to climb it. You suffer from a movement delay of 1.5 with this trait.\n \ + Your total climb time is expected to be 17.5 seconds. Tools may reduce this. \n\n \ + This likewise allows descending walls, provided you're facing an empty space and standing on \ + a climbable wall. To climbe like so, use the verb 'Climb Down Wall' in IC tab!" + cost = 0 + custom_only = FALSE + allowed_species = list(SPECIES_XENOCHIMERA, SPECIES_CUSTOM) //So that we avoid needless bloat for xenochim + excludes = list(/datum/trait/positive/wall_climber_pro, /datum/trait/positive/wall_climber) + +/datum/trait/positive/wall_climber_pro + name = "Climber, Professional" + desc = "You can climb certain walls without tools! You are a professional rock climber at this, letting you climb almost twice as fast!" + tutorial = "You must approach a wall and right click it and select the \ + 'climb wall' verb to climb it. Your movement delay is just 1.25 with this trait.\n \ + Your climb time is expected to be 9 seconds. Tools may reduce this. \n\n \ + This likewise allows descending walls, provided you're facing an empty space and standing on \ + a climbable wall. To climbe like so, use the verb 'Climb Down Wall' in IC tab!" + cost = 2 + custom_only = FALSE + var_changes = list("climbing_delay" = 1.25) + varchange_type = TRAIT_VARCHANGE_LESS_BETTER + excludes = list(/datum/trait/positive/wall_climber,/datum/trait/positive/wall_climber_natural) + +// This feels jank, but it's the cleanest way I could do TRAIT_VARCHANGE_LESS_BETTER while having a boolean var change +// Alternate would've been banned_species = list(SPECIES_TAJ, SPECIES_VASSILISIAN) +// Opted for this as it's "future proof" +/datum/trait/positive/wall_climber_pro/apply(var/datum/species/S,var/mob/living/carbon/human/H) + ..() + S.can_climb = TRUE diff --git a/code/modules/mob/living/living_defines_vr.dm b/code/modules/mob/living/living_defines_vr.dm index 2a547811c2..d20b8555fc 100644 --- a/code/modules/mob/living/living_defines_vr.dm +++ b/code/modules/mob/living/living_defines_vr.dm @@ -14,3 +14,5 @@ //custom temperature discomfort vars var/list/custom_heat = list() var/list/custom_cold = list() + var/can_climb = FALSE //Checked by turfs when using climb_wall(). Defined here for silicons and simple mobs + var/climbing_delay = 1.5 //By default, mobs climb at quarter speed. To be overriden by specific simple mobs or species speed diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/catslug.dm b/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/catslug.dm index 6b4b514e28..4f087411f3 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/catslug.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/catslug.dm @@ -43,6 +43,8 @@ mob_size = MOB_SMALL friendly = list("hugs") see_in_dark = 8 + can_climb = TRUE + climbing_delay = 2.0 ID_provided = TRUE diff --git a/code/modules/multiz/movement_vr.dm b/code/modules/multiz/movement_vr.dm index a8dde43ee9..7e4248e052 100644 --- a/code/modules/multiz/movement_vr.dm +++ b/code/modules/multiz/movement_vr.dm @@ -82,3 +82,238 @@ return 0 if(direction == DOWN) //on a turf above, trying to enter return 1 + +/turf/simulated/proc/climb_wall() + set name = "Climb Wall" + set desc = "Using nature's gifts or technology, scale that wall!" + set category = "Object" + set src in oview(1) + + if(!istype(usr, /mob/living)) return //Why would ghosts want to climb? + var/mob/living/L = usr + var/climbing_delay_min = L.climbing_delay + var/fall_chance = 0 + var/drop_our_held = FALSE + var/nutrition_cost = 50 //Climbing up is harder! + + //Checking if there's any point trying to climb + var/turf/above_wall = GetAbove(src) + if(L.nutrition <= nutrition_cost) + to_chat(L, SPAN_WARNING("You [L.isSynthetic() ? "lack the energy" : "are too hungry"] for such strenous activities!")) + return + if(!above_wall) //No multiZ + to_chat(L, SPAN_NOTICE("There's nothing interesting over this cliff!")) + return + var/turf/above_mob = GetAbove(L) //Making sure we got headroom + if(!above_mob.CanZPass(L, UP)) + to_chat(L, SPAN_WARNING("\The [above_mob] blocks your way.")) + return + if(above_wall.density) //We check density rather than type since some walls dont have a floor on top. + to_chat(L, SPAN_WARNING("\The [above_wall] blocks your way.")) + return + if(LAZYLEN(above_wall.contents) > 30) //We avoid checking the contents if it's too cluttered to avoid issues + to_chat(L, SPAN_WARNING("\The [above_wall] is too cluttered to climb onto!")) + return + for(var/atom/A in above_wall.contents) + if(A.density) + to_chat(L, SPAN_WARNING("\The [A.name] blocks your way!")) + return + + //human mobs got species and can wear special equipment + //We give them some snowflake treatment as a consequence + if(ishuman(L)) + var/permit_human = FALSE + var/mob/living/carbon/human/H = L + if(H.species.climbing_delay < H.climbing_delay) + climbing_delay_min = H.species.climbing_delay + var/list/gear = list(H.head, H.wear_mask, H.wear_suit, H.w_uniform, + H.gloves, H.shoes, H.belt, H.get_active_hand(), H.get_inactive_hand()) + if(H.can_climb || H.species.can_climb) + permit_human = TRUE + for(var/obj/item/I in gear) + if(I.rock_climbing) + permit_human = TRUE + if(I.climbing_delay > climbing_delay_min) + climbing_delay_min = I.climbing_delay //We get the maximum possible speedup out of worn equipment + if(!permit_human) + var/sure = tgui_alert(H,"Are you sure you want to try without tools? It's VERY LIKELY \ + you will fall and get hurt. More agile species might have better luck", "Second Thoughts", list("Bring it!", "Stay grounded")) + if(sure == "Stay grounded") return + fall_chance = clamp(100 - H.species.agility, 40, 90) //This should be 80 for most species. Traceur would reduce to 10%, so clamping higher + //If not a human mob, must be simple or silicon. They got a var stored on their mob we can check + else if(!L.can_climb) + var/sure = tgui_alert(L,"Are you sure you want to try without tools? It's VERY LIKELY \ + you will fall and get hurt. More agile species might have better luck", "Second Thoughts", list("Bring it!", "Stay grounded")) + if(sure == "Stay grounded") return + if(isrobot(L)) + fall_chance = 80 // Robots get no mercy + else + fall_chance = 55 //Simple mobs do. + climbing_delay_min = 2 + //Catslugs are a snowflake case because of references. + if(istype(L, /mob/living/simple_mob/vore/alienanimals/catslug)) + var/obj/O = L.get_active_hand() + if(istype(O, /obj/item/weapon/material/twohanded/spear)) + var/choice = tgui_alert(L, "Use your spear to climb faster? This will drop and break it!", "Scug Tactics", list("Yes!", "No")) + if(choice == "Yes!") + drop_our_held = TRUE + climbing_delay_min = 0.75 + + //We proceed with the actual climbing! + // ################### CLIMB TIME BELOW: ############################# + // Climb time is 3.75 for scugs with spears (spear is dropped) + // Climb time is 5 for Master climbers, Vassilians, Well-geared humans + // Climb time is 9 for Tajara and Professional Climbers + // Climb time is 17.5 Seconds for amateur climbers + // Climb time is 20 seconds for scugs without a spear + // Climb time is 30 for gearless untrained people + var/climb_time = (5 * climbing_delay_min) SECONDS + if(fall_chance) + to_chat(L, SPAN_WARNING("You begin climbing over \The [src]. Getting a grip is exceedingly difficult...")) + climb_time += 20 SECONDS + else + to_chat(L, SPAN_NOTICE("You begin climbing above \The [src]! ")) + if(climbing_delay_min > 1.25) + climb_time += 10 SECONDS + if(climbing_delay_min > 1.0) + climb_time += 2.5 SECONDS + switch(L.nutrition) //Values are 50 lower than the warning icon appearing + if(100 to 200) + to_chat(L, SPAN_NOTICE("Climbing while [L.isSynthetic() ? "low on power" : "hungry"] slows you down")) + climb_time += 1 SECONDS + if(nutrition_cost to 100) + to_chat(L, SPAN_DANGER("You [L.isSynthetic() ? "lack enough power" : "are too hungry"] to climb safely!")) + climb_time +=3 SECONDS + if(fall_chance < 30) + fall_chance = 30 + L.custom_emote(VISIBLE_MESSAGE, "begins to climb up on \The [src]") + var/oops_time = world.time + var/grace_time = 4 SECONDS + to_chat(L, SPAN_WARNING("If you get interrupted after [(grace_time / (1 SECOND))] seconds of climbing, you will fall and hurt yourself, beware!")) + if(do_after(L,climb_time)) + if(prob(fall_chance)) + to_chat(L, SPAN_DANGER("You slipped and fell!")) + L.forceMove(above_mob) + else + if(drop_our_held) + L.drop_item(get_turf(L)) + L.forceMove(above_wall) + L.adjust_nutrition(-nutrition_cost) + + to_chat(L, SPAN_NOTICE("You clambered up successfully!")) + else if(world.time > (oops_time + grace_time)) + L.forceMove(above_mob) + +/mob/living/verb/climb_down() + set name = "Climb down wall" + set desc = "attempt to climb down the wall you are standing on, in direction you're looking" + set category = "IC" + + var/fall_chance = 0 //Increased if we can't actually climb + var/turf/our_turf = get_turf(src) //floor we're standing on + var/climbing_delay_min = src.climbing_delay //We take the lowest climbing delay between mob, species and gear. + var/nutrition_cost = 25 //Descending is easier! + + + //Check if we can even try to climb + if(nutrition <= nutrition_cost) + to_chat(src, SPAN_WARNING("You [isSynthetic() ? "lack the energy" : "are too hungry"] for such strenous activities!")) + return + var/turf/below_wall = GetBelow(our_turf) + if(!below_wall) //No multiZ + to_chat(src, SPAN_NOTICE("There's nothing interesting below us!")) + return + if(!istype(below_wall,/turf/simulated)) //Our var is on simulated turfs, we must enforce this + to_chat(src, SPAN_NOTICE("There's nothing useful to grab onto!")) + return + var/turf/simulated/climbing_surface = below_wall + if(!climbing_surface.density) //passable turfs make no sense to climb + to_chat(src, SPAN_NOTICE("There's nothing climbable below us!")) + return + var/turf/front_of_us = get_step(src, dir) //We get the spot we are facing + if(!front_of_us.CanZPass(src, DOWN)) //Makes sure where we're climbing isnt blocked by a tile or there's a wall below it. + to_chat(src, SPAN_NOTICE("\The [front_of_us] blocks your way in this direction!")) + return + var/turf/destination = GetBelow(front_of_us) + if(isopenspace(destination)) //We don't allow descending more than 1 Z at a time + to_chat(src, SPAN_NOTICE("You're too high up to climb down from here! Find a more gentle descent!")) + return + + //Determining whether we should be able to climb safely and how fast + if(ishuman(src)) + var/permit_human = FALSE + var/mob/living/carbon/human/H = src + if(H.species.climbing_delay < H.climbing_delay) + climbing_delay_min = H.species.climbing_delay + var/list/gear = list(H.head, H.wear_mask, H.wear_suit, H.w_uniform, + H.gloves, H.shoes, H.belt, H.get_active_hand(), H.get_inactive_hand()) + if(H.can_climb || H.species.can_climb) + permit_human = TRUE + for(var/obj/item/I in gear) + if(I.rock_climbing) + permit_human = TRUE + if(I.climbing_delay > climbing_delay_min) + climbing_delay_min = I.climbing_delay //We get the maximum possible speedup out of worn equipment + if(!permit_human) + var/sure = tgui_alert(H,"Are you sure you want to try without tools? It's VERY LIKELY \ + you will fall and get hurt. More agile species might have better luck", "Second Thoughts", list("Bring it!", "Stay grounded")) + if(sure == "Stay grounded") return + fall_chance = clamp(100 - H.species.agility, 40, 90) //This should be 80 for most species. Traceur would reduce to 10%, so clamping higher + //If not a human mob, must be simple or silicon. They got a var stored on their mob we can check + else if(!src.can_climb) + var/sure = tgui_alert(src,"Are you sure you want to try without tools? It's VERY LIKELY \ + you will fall and get hurt. More agile species might have better luck", "Second Thoughts", list("Bring it!", "Stay grounded")) + if(sure == "Stay grounded") return + if(isrobot(src)) + fall_chance = 80 // Robots get no mercy + else + fall_chance = 55 //Simple mobs do. + climbing_delay_min = 2 + //This time, scugs get no snowflake treatment. We're climbing DOWN, not up! + + //We proceed with the actual climbing! + // ################### CLIMB TIME BELOW: ############################# + // Climb time is 3.75 for scugs with spears (spear is dropped) + // Climb time is 5 for Master climbers, Vassilians, Well-geared humans + // Climb time is 9 for Tajara and Professional Climbers + // Climb time is 17.5 Seconds for amateur climbers + // Climb time is 20 seconds for scugs without a spear + // Climb time is 30 for gearless untrained people + var/climb_time = (5 * climbing_delay_min) SECONDS + if(fall_chance) + to_chat(src, SPAN_WARNING("You begin climbing down along \The [below_wall]. Getting a grip is exceedingly difficult...")) + climb_time += 20 SECONDS + else + to_chat(src, SPAN_NOTICE("You begin climbing down \The [below_wall]! ")) + if(climbing_delay_min > 1.25) + climb_time += 10 SECONDS + if(climbing_delay_min > 1.0) + climb_time += 2.5 SECONDS + switch(nutrition) //Values are 50 lower than the warning icon appearing + if(100 to 200) + to_chat(src, SPAN_NOTICE("Climbing while [isSynthetic() ? "low on power" : "hungry"] slows you down")) + climb_time += 1 SECONDS + if(nutrition_cost to 100) + to_chat(src, SPAN_DANGER("You [isSynthetic() ? "lack enough power" : "are too hungry"] to climb safely!")) + climb_time +=3 SECONDS + if(fall_chance < 30) + fall_chance = 30 + + if(!climbing_surface.climbable) + to_chat(src, SPAN_DANGER("\The [climbing_surface] is not suitable for climbing! Even for a master climber, this is risky!")) + if(fall_chance < 75 ) + fall_chance = 75 + src.custom_emote(VISIBLE_MESSAGE, "begins to climb down along \The [below_wall]") + var/oops_time = world.time + var/grace_time = 3 SECONDS + to_chat(src, SPAN_WARNING("If you get interrupted after [(grace_time / (1 SECOND))] seconds of climbing, you will fall and hurt yourself, beware!")) + if(do_after(src,climb_time)) + if(prob(fall_chance)) + to_chat(src, SPAN_DANGER("You slipped and fell!")) + src.forceMove(front_of_us) + else + src.forceMove(destination) + to_chat(src, SPAN_NOTICE("You descended successfully!")) + adjust_nutrition(-nutrition_cost) + else if(world.time > (oops_time + grace_time)) + src.forceMove(front_of_us)