mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2026-07-13 17:22:41 +01:00
Adds New PoI Features + Bonus PoI (#5796)
* Adds cliffs. * Finishes lava and polishes the other things. * Hopefully finished with all the new shinies.
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
GLOBAL_LIST_EMPTY(cliff_icon_cache)
|
||||
|
||||
/*
|
||||
Cliffs give a visual illusion of depth by seperating two places while presenting a 'top' and 'bottom' side.
|
||||
|
||||
Mobs moving into a cliff from the bottom side will simply bump into it and be denied moving into the tile,
|
||||
where as mobs moving into a cliff from the top side will 'fall' off the cliff, forcing them to the bottom, causing significant damage and stunning them.
|
||||
|
||||
Mobs can climb this while wearing climbing equipment by clickdragging themselves onto a cliff, as if it were a table.
|
||||
|
||||
Flying mobs can pass over all cliffs with no risk of falling.
|
||||
|
||||
Projectiles and thrown objects can pass, however if moving upwards, there is a chance for it to be stopped by the cliff.
|
||||
This makes fighting something that is on top of a cliff more challenging.
|
||||
|
||||
As a note, dir points upwards, e.g. pointing WEST means the left side is 'up', and the right side is 'down'.
|
||||
|
||||
When mapping these in, be sure to give at least a one tile clearance, as NORTH facing cliffs expand to
|
||||
two tiles on initialization, and which way a cliff is facing may change during maploading.
|
||||
*/
|
||||
|
||||
/obj/structure/cliff
|
||||
name = "cliff"
|
||||
desc = "A steep rock ledge. You might be able to climb it if you feel bold enough."
|
||||
description_info = "Walking off the edge of a cliff while on top will cause you to fall off, causing severe injury.<br>\
|
||||
You can climb this cliff if wearing special climbing equipment, by click-dragging yourself onto the cliff.<br>\
|
||||
Projectiles traveling up a cliff may hit the cliff instead, making it more difficult to fight something \
|
||||
on top."
|
||||
icon = 'icons/obj/flora/rocks.dmi'
|
||||
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
opacity = FALSE
|
||||
climbable = TRUE
|
||||
climb_delay = 10 SECONDS
|
||||
block_turf_edges = TRUE // Don't want turf edges popping up from the cliff edge.
|
||||
register_as_dangerous_object = TRUE
|
||||
|
||||
var/icon_variant = null // Used to make cliffs less repeative by having a selection of sprites to display.
|
||||
var/corner = FALSE // Used for icon things.
|
||||
var/ramp = FALSE // Ditto.
|
||||
var/bottom = FALSE // Used for 'bottom' typed cliffs, to avoid infinite cliffs, and for icons.
|
||||
|
||||
var/is_double_cliff = FALSE // Set to true when making the two-tile cliffs, used for projectile checks.
|
||||
var/uphill_penalty = 30 // Odds of a projectile not making it up the cliff.
|
||||
|
||||
// These arrange their sprites at runtime, as opposed to being statically placed in the map file.
|
||||
/obj/structure/cliff/automatic
|
||||
icon_state = "cliffbuilder"
|
||||
dir = NORTH
|
||||
|
||||
/obj/structure/cliff/automatic/corner
|
||||
icon_state = "cliffbuilder-corner"
|
||||
dir = NORTHEAST
|
||||
corner = TRUE
|
||||
|
||||
// Tiny part that doesn't block, used for making 'ramps'.
|
||||
/obj/structure/cliff/automatic/ramp
|
||||
icon_state = "cliffbuilder-ramp"
|
||||
dir = NORTHEAST
|
||||
density = FALSE
|
||||
ramp = TRUE
|
||||
|
||||
// Made automatically as needed by automatic cliffs.
|
||||
/obj/structure/cliff/bottom
|
||||
bottom = TRUE
|
||||
|
||||
/obj/structure/cliff/automatic/Initialize()
|
||||
..()
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
// Paranoid about the maploader, direction is very important to cliffs, since they may get bigger if initialized while facing NORTH.
|
||||
/obj/structure/cliff/automatic/LateInitialize()
|
||||
if(dir in GLOB.cardinal)
|
||||
icon_variant = pick("a", "b", "c")
|
||||
|
||||
if(dir & NORTH && !bottom) // North-facing cliffs require more cliffs to be made.
|
||||
make_bottom()
|
||||
|
||||
update_icon()
|
||||
|
||||
/obj/structure/cliff/proc/make_bottom()
|
||||
// First, make sure there's room to put the bottom side.
|
||||
var/turf/T = locate(x, y - 1, z)
|
||||
if(!istype(T))
|
||||
return FALSE
|
||||
|
||||
// Now make the bottom cliff have mostly the same variables.
|
||||
var/obj/structure/cliff/bottom/bottom = new(T)
|
||||
is_double_cliff = TRUE
|
||||
climb_delay /= 2 // Since there are two cliffs to climb when going north, both take half the time.
|
||||
|
||||
bottom.dir = dir
|
||||
bottom.is_double_cliff = TRUE
|
||||
bottom.climb_delay = climb_delay
|
||||
bottom.icon_variant = icon_variant
|
||||
bottom.corner = corner
|
||||
bottom.ramp = ramp
|
||||
bottom.layer = layer - 0.1
|
||||
bottom.density = density
|
||||
bottom.update_icon()
|
||||
|
||||
/obj/structure/cliff/set_dir(new_dir)
|
||||
..()
|
||||
update_icon()
|
||||
|
||||
/obj/structure/cliff/update_icon()
|
||||
icon_state = "cliff-[dir][icon_variant][bottom ? "-bottom" : ""][corner ? "-corner" : ""][ramp ? "-ramp" : ""]"
|
||||
|
||||
// Now for making the top-side look like a different turf.
|
||||
var/turf/T = get_step(src, dir)
|
||||
if(!istype(T))
|
||||
return
|
||||
|
||||
var/subtraction_icon_state = "[icon_state]-subtract"
|
||||
var/cache_string = "[icon_state]_[T.icon]_[T.icon_state]"
|
||||
if(T && subtraction_icon_state in icon_states(icon))
|
||||
cut_overlays()
|
||||
// If we've made the same icon before, just recycle it.
|
||||
if(cache_string in GLOB.cliff_icon_cache)
|
||||
add_overlay(GLOB.cliff_icon_cache[cache_string])
|
||||
|
||||
else // Otherwise make a new one, but only once.
|
||||
var/icon/underlying_ground = icon(T.icon, T.icon_state, T.dir)
|
||||
var/icon/subtract = icon(icon, subtraction_icon_state)
|
||||
underlying_ground.Blend(subtract, ICON_SUBTRACT)
|
||||
var/image/final = image(underlying_ground)
|
||||
final.layer = src.layer - 0.2
|
||||
GLOB.cliff_icon_cache[cache_string] = final
|
||||
add_overlay(final)
|
||||
|
||||
|
||||
// Movement-related code.
|
||||
|
||||
/obj/structure/cliff/CanPass(atom/movable/mover, turf/target, height = 0, air_group = 0)
|
||||
if(air_group || height == 0)
|
||||
return TRUE // Airflow can always pass.
|
||||
|
||||
else if(isliving(mover))
|
||||
var/mob/living/L = mover
|
||||
if(L.hovering) // Flying mobs can always pass.
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
// Projectiles and objects flying 'upward' have a chance to hit the cliff instead, wasting the shot.
|
||||
else if(istype(mover, /obj))
|
||||
var/obj/O = mover
|
||||
if(check_shield_arc(src, dir, O)) // This is actually for mobs but it will work for our purposes as well.
|
||||
if(prob(uphill_penalty / (1 + is_double_cliff) )) // Firing upwards facing NORTH means it will likely have to pass through two cliffs, so the chance is halved.
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/structure/cliff/Bumped(atom/A)
|
||||
if(isliving(A))
|
||||
var/mob/living/L = A
|
||||
if(should_fall(L))
|
||||
fall_off_cliff(L)
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/structure/cliff/proc/should_fall(mob/living/L)
|
||||
if(L.hovering)
|
||||
return FALSE
|
||||
|
||||
var/turf/T = get_turf(L)
|
||||
if(T && get_dir(T, loc) & reverse_dir[dir]) // dir points 'up' the cliff, e.g. cliff pointing NORTH will cause someone to fall if moving SOUTH into it.
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/structure/cliff/proc/fall_off_cliff(mob/living/L)
|
||||
if(!istype(L))
|
||||
return FALSE
|
||||
var/turf/T = get_step(src, reverse_dir[dir])
|
||||
var/displaced = FALSE
|
||||
|
||||
if(dir in list(EAST, WEST)) // Apply an offset if flying sideways, to help maintain the illusion of depth.
|
||||
for(var/i = 1 to 2)
|
||||
var/turf/new_T = locate(T.x, T.y - i, T.z)
|
||||
if(!new_T || locate(/obj/structure/cliff) in new_T)
|
||||
break
|
||||
T = new_T
|
||||
displaced = TRUE
|
||||
|
||||
if(istype(T))
|
||||
visible_message(span("danger", "\The [L] falls off \the [src]!"))
|
||||
L.forceMove(T)
|
||||
|
||||
// Do the actual hurting. Double cliffs do halved damage due to them most likely hitting twice.
|
||||
var/harm = !is_double_cliff ? 1 : 0.5
|
||||
if(istype(L.buckled, /obj/vehicle)) // People falling off in vehicles will take less damage, but will damage the vehicle severely.
|
||||
var/obj/vehicle/vehicle = L.buckled
|
||||
vehicle.adjust_health(40 * harm)
|
||||
to_chat(L, span("warning", "\The [vehicle] absorbs some of the impact, damaging it."))
|
||||
harm /= 2
|
||||
|
||||
playsound(L, 'sound/effects/break_stone.ogg', 70, 1)
|
||||
L.Weaken(5 * harm)
|
||||
var/fall_time = 3
|
||||
if(displaced) // Make the fall look more natural when falling sideways.
|
||||
L.pixel_z = 32 * 2
|
||||
animate(L, pixel_z = 0, time = fall_time)
|
||||
sleep(fall_time) // A brief delay inbetween the two sounds helps sell the 'ouch' effect.
|
||||
playsound(L, "punch", 70, 1)
|
||||
shake_camera(L, 1, 1)
|
||||
visible_message(span("danger", "\The [L] hits the ground!"))
|
||||
|
||||
// The bigger they are, the harder they fall.
|
||||
// They will take at least 20 damage at the minimum, and tries to scale up to 40% of their max health.
|
||||
// This scaling is capped at 100 total damage, which occurs if the thing that fell has more than 250 health.
|
||||
var/damage = between(20, L.getMaxHealth() * 0.4, 100)
|
||||
var/target_zone = ran_zone()
|
||||
var/blocked = L.run_armor_check(target_zone, "melee") * harm
|
||||
var/soaked = L.get_armor_soak(target_zone, "melee") * harm
|
||||
|
||||
L.apply_damage(damage * harm, BRUTE, target_zone, blocked, soaked, used_weapon=src)
|
||||
|
||||
// Now fall off more cliffs below this one if they exist.
|
||||
var/obj/structure/cliff/bottom_cliff = locate() in T
|
||||
if(bottom_cliff)
|
||||
visible_message(span("danger", "\The [L] rolls down towards \the [bottom_cliff]!"))
|
||||
sleep(5)
|
||||
bottom_cliff.fall_off_cliff(L)
|
||||
|
||||
/obj/structure/cliff/can_climb(mob/living/user, post_climb_check = FALSE)
|
||||
// Cliff climbing requires climbing gear.
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
var/obj/item/clothing/shoes/shoes = H.shoes
|
||||
if(shoes && shoes.rock_climbing)
|
||||
return ..() // Do the other checks too.
|
||||
|
||||
to_chat(user, span("warning", "\The [src] is too steep to climb unassisted."))
|
||||
return FALSE
|
||||
|
||||
// This tells AI mobs to not be dumb and step off cliffs willingly.
|
||||
/obj/structure/cliff/is_safe_to_step(mob/living/L)
|
||||
if(should_fall(L))
|
||||
return FALSE
|
||||
return ..()
|
||||
@@ -0,0 +1,179 @@
|
||||
//Chain link fences
|
||||
//Sprites ported from /VG/
|
||||
|
||||
#define CUT_TIME 10 SECONDS
|
||||
#define CLIMB_TIME 5 SECONDS
|
||||
|
||||
#define NO_HOLE 0 //section is intact
|
||||
#define MEDIUM_HOLE 1 //medium hole in the section - can climb through
|
||||
#define LARGE_HOLE 2 //large hole in the section - can walk through
|
||||
#define MAX_HOLE_SIZE LARGE_HOLE
|
||||
|
||||
/obj/structure/fence
|
||||
name = "fence"
|
||||
desc = "A chain link fence. Not as effective as a wall, but generally it keeps people out."
|
||||
description_info = "Projectiles can freely pass fences."
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
|
||||
icon = 'icons/obj/fence.dmi'
|
||||
icon_state = "straight"
|
||||
|
||||
var/cuttable = TRUE
|
||||
var/hole_size= NO_HOLE
|
||||
var/invulnerable = FALSE
|
||||
|
||||
/obj/structure/fence/Initialize()
|
||||
update_cut_status()
|
||||
return ..()
|
||||
|
||||
/obj/structure/fence/examine(mob/user)
|
||||
. = ..()
|
||||
|
||||
switch(hole_size)
|
||||
if(MEDIUM_HOLE)
|
||||
user.show_message("There is a large hole in \the [src].")
|
||||
if(LARGE_HOLE)
|
||||
user.show_message("\The [src] has been completely cut through.")
|
||||
|
||||
/obj/structure/fence/get_description_interaction()
|
||||
var/list/results = list()
|
||||
if(cuttable && !invulnerable && hole_size < MAX_HOLE_SIZE)
|
||||
results += "[desc_panel_image("wirecutters")]to [hole_size > NO_HOLE ? "expand the":"cut a"] hole into the fence, allowing passage."
|
||||
return results
|
||||
|
||||
/obj/structure/fence/end
|
||||
icon_state = "end"
|
||||
cuttable = FALSE
|
||||
|
||||
/obj/structure/fence/corner
|
||||
icon_state = "corner"
|
||||
cuttable = FALSE
|
||||
|
||||
/obj/structure/fence/post
|
||||
icon_state = "post"
|
||||
cuttable = FALSE
|
||||
|
||||
/obj/structure/fence/cut/medium
|
||||
icon_state = "straight_cut2"
|
||||
hole_size = MEDIUM_HOLE
|
||||
|
||||
/obj/structure/fence/cut/large
|
||||
icon_state = "straight_cut3"
|
||||
hole_size = LARGE_HOLE
|
||||
|
||||
// Projectiles can pass through fences.
|
||||
/obj/structure/fence/CanPass(atom/movable/mover, turf/target, height = 0, air_group = 0)
|
||||
if(istype(mover, /obj/item/projectile))
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/obj/structure/fence/attackby(obj/item/W, mob/user)
|
||||
if(W.is_wirecutter())
|
||||
if(!cuttable)
|
||||
to_chat(user, span("warning", "This section of the fence can't be cut."))
|
||||
return
|
||||
if(invulnerable)
|
||||
to_chat(user, span("warning", "This fence is too strong to cut through."))
|
||||
return
|
||||
var/current_stage = hole_size
|
||||
if(current_stage >= MAX_HOLE_SIZE)
|
||||
to_chat(user, span("notice", "This fence has too much cut out of it already."))
|
||||
return
|
||||
|
||||
user.visible_message(span("danger", "\The [user] starts cutting through \the [src] with \the [W]."),\
|
||||
span("danger", "You start cutting through \the [src] with \the [W]."))
|
||||
playsound(src, W.usesound, 50, 1)
|
||||
|
||||
if(do_after(user, CUT_TIME * W.toolspeed, target = src))
|
||||
if(current_stage == hole_size)
|
||||
switch(++hole_size)
|
||||
if(MEDIUM_HOLE)
|
||||
visible_message(span("notice", "\The [user] cuts into \the [src] some more."))
|
||||
to_chat(user, span("notice", "You could probably fit yourself through that hole now. Although climbing through would be much faster if you made it even bigger."))
|
||||
climbable = TRUE
|
||||
if(LARGE_HOLE)
|
||||
visible_message(span("notice", "\The [user] completely cuts through \the [src]."))
|
||||
to_chat(user, span("notice", "The hole in \the [src] is now big enough to walk through."))
|
||||
climbable = FALSE
|
||||
update_cut_status()
|
||||
return TRUE
|
||||
|
||||
/obj/structure/fence/proc/update_cut_status()
|
||||
if(!cuttable)
|
||||
return
|
||||
density = TRUE
|
||||
|
||||
switch(hole_size)
|
||||
if(NO_HOLE)
|
||||
icon_state = initial(icon_state)
|
||||
if(MEDIUM_HOLE)
|
||||
icon_state = "straight_cut2"
|
||||
if(LARGE_HOLE)
|
||||
icon_state = "straight_cut3"
|
||||
density = FALSE
|
||||
|
||||
//FENCE DOORS
|
||||
|
||||
/obj/structure/fence/door
|
||||
name = "fence door"
|
||||
desc = "Not very useful without a real lock."
|
||||
icon_state = "door_closed"
|
||||
cuttable = FALSE
|
||||
var/open = FALSE
|
||||
var/locked = FALSE
|
||||
|
||||
/obj/structure/fence/door/Initialize()
|
||||
update_door_status()
|
||||
return ..()
|
||||
|
||||
/obj/structure/fence/door/opened
|
||||
icon_state = "door_opened"
|
||||
open = TRUE
|
||||
density = TRUE
|
||||
|
||||
/obj/structure/fence/door/locked
|
||||
desc = "It looks like it has a strong padlock attached."
|
||||
locked = TRUE
|
||||
|
||||
/obj/structure/fence/door/attack_hand(mob/user)
|
||||
if(can_open(user))
|
||||
toggle(user)
|
||||
else
|
||||
to_chat(user, span("warning", "\The [src] is [!open ? "locked" : "stuck open"]."))
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/structure/fence/door/proc/toggle(mob/user)
|
||||
switch(open)
|
||||
if(FALSE)
|
||||
visible_message(span("notice", "\The [user] opens \the [src]."))
|
||||
open = TRUE
|
||||
if(TRUE)
|
||||
visible_message(span("notice", "\The [user] closes \the [src]."))
|
||||
open = FALSE
|
||||
|
||||
update_door_status()
|
||||
playsound(src, 'sound/machines/click.ogg', 100, 1)
|
||||
|
||||
/obj/structure/fence/door/proc/update_door_status()
|
||||
switch(open)
|
||||
if(FALSE)
|
||||
density = TRUE
|
||||
icon_state = "door_closed"
|
||||
if(TRUE)
|
||||
density = FALSE
|
||||
icon_state = "door_opened"
|
||||
|
||||
/obj/structure/fence/door/proc/can_open(mob/user)
|
||||
if(locked)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
#undef CUT_TIME
|
||||
#undef CLIMB_TIME
|
||||
|
||||
#undef NO_HOLE
|
||||
#undef MEDIUM_HOLE
|
||||
#undef LARGE_HOLE
|
||||
#undef MAX_HOLE_SIZE
|
||||
@@ -238,6 +238,18 @@
|
||||
name = "\improper EMERGENT INTELLIGENCE DETAILS"
|
||||
icon_state = "rogueai"
|
||||
|
||||
/obj/structure/sign/warning/falling
|
||||
name = "\improper FALL HAZARD"
|
||||
icon_state = "falling"
|
||||
|
||||
/obj/structure/sign/warning/lava
|
||||
name = "\improper MOLTEN SURFACE"
|
||||
icon_state = "lava"
|
||||
|
||||
/obj/structure/sign/warning/acid
|
||||
name = "\improper ACIDIC SURFACE"
|
||||
icon_state = "acid"
|
||||
|
||||
/obj/structure/sign/redcross
|
||||
name = "medbay"
|
||||
desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here."
|
||||
|
||||
Reference in New Issue
Block a user