Skip to content

Commit

Permalink
You can no longer stack machines on one tile (yogstation13#19683)
Browse files Browse the repository at this point in the history
* Ports climbable & blocked turf

Adds the climbable element to replace the copy paste code between machines and structures
Ports the code improvements to blocked turf checks, commenting back in its uses that tattax had added but couldnt get functional (fixes ethereal jaunt & hierophant teleporting)
Ports over LADYADDASSOCLIST code improvements, this has no effect in-game and mostly in the codebase, it's only ever used in two other locations total.
Fixes being able to stack several machines on one tile

* fixes climbing on crates

* Update climbable.dm

* merge conflicts r awesome
  • Loading branch information
JohnFulpWillard authored Jul 28, 2023
1 parent 77ddc84 commit b5718d1
Show file tree
Hide file tree
Showing 53 changed files with 358 additions and 278 deletions.
13 changes: 13 additions & 0 deletions code/__DEFINES/directional.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Byond direction defines, because I want to put them somewhere.
// #define NORTH 1
// #define SOUTH 2
// #define EAST 4
// #define WEST 8

#define TEXT_NORTH "[NORTH]"
#define TEXT_SOUTH "[SOUTH]"
#define TEXT_EAST "[EAST]"
#define TEXT_WEST "[WEST]"

/// Inverse direction, taking into account UP|DOWN if necessary.
#define REVERSE_DIR(dir) ( ((dir & 85) << 1) | ((dir & 170) >> 1) )
12 changes: 0 additions & 12 deletions code/__DEFINES/misc.dm
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
// Byond direction defines, because I want to put them somewhere.
// #define NORTH 1
// #define SOUTH 2
// #define EAST 4
// #define WEST 8

#define TEXT_NORTH "[NORTH]"
#define TEXT_SOUTH "[SOUTH]"
#define TEXT_EAST "[EAST]"
#define TEXT_WEST "[WEST]"


//Human Overlays Indexes/////////
#define MUTATIONS_LAYER 28 //mutations. Tk headglows, cold resistance glow, etc
#define BODY_BEHIND_LAYER 27 //certain mutantrace features (tail when looking south) that must appear behind the body parts
Expand Down
5 changes: 5 additions & 0 deletions code/__DEFINES/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,8 @@
/// trait associated to being held in a chokehold
#define CHOKEHOLD_TRAIT "chokehold"
#define CRYO_TRAIT "cryo_trait"
/// Trait applied by element
#define ELEMENT_TRAIT(source) "element_trait_[source]"

// unique trait sources, still defines
#define CLONING_POD_TRAIT "cloning-pod"
Expand Down Expand Up @@ -439,5 +441,8 @@
///every object that is currently the active storage of some client mob has this trait
#define TRAIT_ACTIVE_STORAGE "active_storage"

/// Climbable trait, given and taken by the climbable element when added or removed. Exists to be easily checked via HAS_TRAIT().
#define TRAIT_CLIMBABLE "trait_climbable"

///Organ traits
#define TRAIT_BALLMER_SCIENTIST "ballmer_scientist"
6 changes: 5 additions & 1 deletion code/__HELPERS/_lists.dm
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@
///Returns the list if it's actually a valid list, otherwise will initialize it
#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
#define reverseList(L) reverseRange(L.Copy())
#define LAZYADDASSOC(L, K, V) if(!L) { L = list(); } L[K] += list(V);
///Adds to the item K the value V, if the list is null it will initialize it
#define LAZYADDASSOC(L, K, V) if(!L) { L = list(); } L[K] += V;
///This is used to add onto lazy assoc list when the value you're adding is a /list/. This one has extra safety over lazyaddassoc because the value could be null (and thus cant be used to += objects)
#define LAZYADDASSOCLIST(L, K, V) if(!L) { L = list(); } L[K] += list(V);
///Removes the value V from the item K, if the item K is empty will remove it from the list, if the list is empty will set the list to null
#define LAZYREMOVEASSOC(L, K, V) if(L) { if(L[K]) { L[K] -= V; if(!length(L[K])) L -= K; } if(!length(L)) L = null; }

/// Passed into BINARY_INSERT to compare keys
Expand Down
2 changes: 1 addition & 1 deletion code/__HELPERS/game.dm
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,7 @@
continue

if (!isspaceturf(found_turf))
if (!is_blocked_turf(found_turf))
if (!found_turf.is_blocked_turf())
possible_loc.Add(found_turf)

// Need at least one free location.
Expand Down
15 changes: 3 additions & 12 deletions code/__HELPERS/unsorted.dm
Original file line number Diff line number Diff line change
Expand Up @@ -504,15 +504,6 @@ Turf and target are separate in case you want to teleport some distance from a t

return 1

/proc/is_blocked_turf(turf/T, exclude_mobs)
if(T.density)
return 1
for(var/i in T)
var/atom/A = i
if(A.density && (!exclude_mobs || !ismob(A)))
return 1
return 0

/proc/is_anchored_dense_turf(turf/T) //like the older version of the above, fails only if also anchored
if(T.density)
return 1
Expand All @@ -526,7 +517,7 @@ Turf and target are separate in case you want to teleport some distance from a t
var/base_dir = get_dir(ref, get_step_towards(ref,trg))
var/turf/temp = get_step_towards(ref,trg)

if(is_blocked_turf(temp))
if(temp.is_blocked_turf())
var/dir_alt1 = turn(base_dir, 90)
var/dir_alt2 = turn(base_dir, -90)
var/turf/turf_last1 = temp
Expand All @@ -535,10 +526,10 @@ Turf and target are separate in case you want to teleport some distance from a t
var/breakpoint = 0

while(!free_tile && breakpoint < 10)
if(!is_blocked_turf(turf_last1))
if(!turf_last1.is_blocked_turf())
free_tile = turf_last1
break
if(!is_blocked_turf(turf_last2))
if(!turf_last2.is_blocked_turf())
free_tile = turf_last2
break
turf_last1 = get_step(turf_last1,dir_alt1)
Expand Down
4 changes: 2 additions & 2 deletions code/controllers/subsystem/job.dm
Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,7 @@ SUBSYSTEM_DEF(job)
//last hurrah
var/list/avail = list()
for(var/turf/T in A)
if(!is_blocked_turf(T, TRUE))
if(!T.is_blocked_turf(TRUE))
avail += T
if(avail.len)
destination = pick(avail)
Expand All @@ -847,7 +847,7 @@ SUBSYSTEM_DEF(job)
var/list/arrivals_turfs = shuffle(get_area_turfs(/area/shuttle/arrival))
if(arrivals_turfs.len)
for(var/turf/T in arrivals_turfs)
if(!is_blocked_turf(T, TRUE))
if(!T.is_blocked_turf(TRUE))
T.JoinPlayerHere(M, FALSE)
return
//last chance, pick ANY spot on arrivals and dump em
Expand Down
2 changes: 1 addition & 1 deletion code/controllers/subsystem/minor_mapping.dm
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ SUBSYSTEM_DEF(minor_mapping)
for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
all_turfs += block(locate(1,1,z), locate(world.maxx,world.maxy,z))
for(var/turf/open/floor/plating/T in all_turfs)
if(is_blocked_turf(T))
if(T.is_blocked_turf())
continue
if(locate(/obj/structure/cable) in T)
exposed_wires += T
Expand Down
2 changes: 1 addition & 1 deletion code/datums/chatmessage.dm
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@
message.maptext = complete_text

// View the message
LAZYADDASSOC(owned_by.seen_messages, message_loc, src)
LAZYADDASSOCLIST(owned_by.seen_messages, message_loc, src)
owned_by.images |= message
animate(message, alpha = 255, time = CHAT_MESSAGE_SPAWN_TIME)

Expand Down
130 changes: 130 additions & 0 deletions code/datums/elements/climbable.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/datum/element/climbable
element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH_ON_HOST_DESTROY // Detach for turfs
argument_hash_start_idx = 2
///Time it takes to climb onto the object
var/climb_time
///Stun duration for when you get onto the object
var/climb_stun
///Assoc list of object being climbed on - climbers. This allows us to check who needs to be shoved off a climbable object when its clicked on.
var/list/current_climbers

/datum/element/climbable/Attach(
datum/target,
climb_time = 2 SECONDS,
climb_stun = 2 SECONDS,
)
. = ..()

if(!isatom(target) || isarea(target))
return ELEMENT_INCOMPATIBLE
src.climb_time = climb_time
src.climb_stun = climb_stun

RegisterSignal(target, COMSIG_ATOM_ATTACK_HAND, PROC_REF(attack_hand))
RegisterSignal(target, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
RegisterSignal(target, COMSIG_MOUSEDROPPED_ONTO, PROC_REF(mousedrop_receive))
RegisterSignal(target, COMSIG_ATOM_BUMPED, PROC_REF(try_speedrun))
ADD_TRAIT(target, TRAIT_CLIMBABLE, ELEMENT_TRAIT(type))

/datum/element/climbable/Detach(datum/target)
UnregisterSignal(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_PARENT_EXAMINE, COMSIG_MOUSEDROPPED_ONTO, COMSIG_ATOM_BUMPED))
REMOVE_TRAIT(target, TRAIT_CLIMBABLE, ELEMENT_TRAIT(type))
return ..()

/datum/element/climbable/proc/on_examine(atom/source, mob/user, list/examine_texts)
SIGNAL_HANDLER
examine_texts += span_notice("[source] looks climbable.")

/datum/element/climbable/proc/can_climb(atom/source, mob/user)
var/dir_step = get_dir(user, source.loc)
//To jump over a railing you have to be standing next to it, not far behind it.
if(source.flags_1 & ON_BORDER_1 && user.loc != source.loc && (dir_step & source.dir) == source.dir)
return FALSE
return TRUE

/datum/element/climbable/proc/attack_hand(atom/climbed_thing, mob/user)
SIGNAL_HANDLER
var/list/climbers = LAZYACCESS(current_climbers, climbed_thing)
for(var/i in climbers)
var/mob/living/structure_climber = i
if(structure_climber == user)
return
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(climbed_thing)
structure_climber.Paralyze(40)
structure_climber.visible_message(span_warning("[structure_climber] is knocked off [climbed_thing]."), span_warning("You're knocked off [climbed_thing]!"), span_hear("You hear a cry from [structure_climber], followed by a slam."))


/datum/element/climbable/proc/climb_structure(atom/climbed_thing, mob/living/user, params)
if(!can_climb(climbed_thing, user))
return
climbed_thing.add_fingerprint(user)
user.visible_message(span_warning("[user] starts climbing onto [climbed_thing]."), \
span_notice("You start climbing onto [climbed_thing]..."))
var/adjusted_climb_time = climb_time
var/adjusted_climb_stun = climb_stun
if(HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //climbing takes twice as long without help from the hands.
adjusted_climb_time *= 2
if(isalien(user))
adjusted_climb_time *= 0.25 //aliens are terrifyingly fast
if(HAS_TRAIT(user, TRAIT_FREERUNNING)) //do you have any idea how fast I am???
adjusted_climb_time *= 0.8
adjusted_climb_stun *= 0.8
LAZYADDASSOCLIST(current_climbers, climbed_thing, user)
if(do_after(user, adjusted_climb_time, climbed_thing))
if(QDELETED(climbed_thing)) //Checking if structure has been destroyed
return
if(do_climb(climbed_thing, user, params))
user.visible_message(span_warning("[user] climbs onto [climbed_thing]."), \
span_notice("You climb onto [climbed_thing]."))
log_combat(user, climbed_thing, "climbed onto")
if(adjusted_climb_stun)
user.Stun(adjusted_climb_stun)
else
to_chat(user, span_warning("You fail to climb onto [climbed_thing]."))
LAZYREMOVEASSOC(current_climbers, climbed_thing, user)


/datum/element/climbable/proc/do_climb(atom/climbed_thing, mob/living/user, params)
if(!can_climb(climbed_thing, user))
return
climbed_thing.set_density(FALSE)
var/dir_step = get_dir(user, climbed_thing.loc)
var/same_loc = climbed_thing.loc == user.loc
// on-border objects can be vaulted over and into the next turf.
// The reverse dir check is for when normal behavior should apply instead (e.g. John Doe hops east of a railing facing west, ending on the same turf as it).
if(climbed_thing.flags_1 & ON_BORDER_1 && (same_loc || !(dir_step & REVERSE_DIR(climbed_thing.dir))))
//it can be vaulted over in two different cardinal directions. we choose one.
if(ISDIAGONALDIR(climbed_thing.dir) && same_loc)
if(params) //we check the icon x and y parameters of the click-drag to determine step_dir.
var/list/modifiers = params2list(params)
var/x_dist = (text2num(LAZYACCESS(modifiers, ICON_X)) - world.icon_size/2) * (climbed_thing.dir & WEST ? -1 : 1)
var/y_dist = (text2num(LAZYACCESS(modifiers, ICON_Y)) - world.icon_size/2) * (climbed_thing.dir & SOUTH ? -1 : 1)
dir_step = (x_dist >= y_dist ? (EAST|WEST) : (NORTH|SOUTH)) & climbed_thing.dir
else //user is being moved by a forced_movement datum. dir_step will be the direction to the forced movement target.
dir_step = get_dir(user, user.force_moving.target)
else
dir_step = get_dir(user, get_step(climbed_thing, climbed_thing.dir))
. = step(user, dir_step)
climbed_thing.set_density(TRUE)

///Handles climbing onto the atom when you click-drag
/datum/element/climbable/proc/mousedrop_receive(atom/climbed_thing, atom/movable/dropped_atom, mob/user, params)
SIGNAL_HANDLER
if(user == dropped_atom && isliving(dropped_atom))
var/mob/living/living_target = dropped_atom
if(isanimal(living_target))
var/mob/living/simple_animal/animal = dropped_atom
if (!animal.dextrous)
return
if(living_target.mobility_flags & MOBILITY_MOVE)
INVOKE_ASYNC(src, PROC_REF(climb_structure), climbed_thing, living_target, params)
return

///Tries to climb onto the target if the forced movement of the mob allows it
/datum/element/climbable/proc/try_speedrun(datum/source, mob/bumpee)
SIGNAL_HANDLER
if(!istype(bumpee))
return
if(bumpee.force_moving?.allow_climbing)
do_climb(source, bumpee)
7 changes: 0 additions & 7 deletions code/datums/forced_movement.dm
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,3 @@
. = TryMove(TRUE)

. = . && (vic.loc != tar.loc)

/mob/Bump(atom/A)
. = ..()
if(force_moving && force_moving.allow_climbing && isstructure(A))
var/obj/structure/S = A
if(S.climbable)
S.do_climb(src)
Loading

0 comments on commit b5718d1

Please sign in to comment.