Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge Upstream 02.02.2025 #1781

Merged
merged 18 commits into from
Feb 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions code/__DEFINES/time_defines.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#define MILLISECONDS *0.01

#define DECISECONDS *1 //the base unit all of these defines are scaled by, because byond uses that as a unit of measurement for some fucking reason

// So you can be all 10 SECONDS
#define SECONDS *10

#define MINUTES *600

#define HOURS *36000

#define TICKS *world.tick_lag

#define SECONDS_TO_LIFE_CYCLES /2

#define DS2TICKS(DS) ((DS)/world.tick_lag)

#define TICKS2DS(T) ((T) TICKS)
19 changes: 0 additions & 19 deletions code/__HELPERS/time.dm
Original file line number Diff line number Diff line change
@@ -1,22 +1,3 @@
#define MILLISECONDS *0.01

#define DECISECONDS *1 //the base unit all of these defines are scaled by, because byond uses that as a unit of measurement for some fucking reason

// So you can be all 10 SECONDS
#define SECONDS *10

#define MINUTES *600

#define HOURS *36000

#define TICKS *world.tick_lag

#define SECONDS_TO_LIFE_CYCLES /2

#define DS2TICKS(DS) ((DS)/world.tick_lag)

#define TICKS2DS(T) ((T) TICKS)

/* This proc should only be used for world/Topic.
* If you want to display the time for which dream daemon has been running ("round time") use worldtime2text.
* If you want to display the canonical station "time" (aka the in-character time of the station) use station_time_timestamp
Expand Down
6 changes: 0 additions & 6 deletions code/__HELPERS/unsorted.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1562,12 +1562,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
if(areas)
. |= T.loc

/proc/turf_clear(turf/T)
for(var/atom/A in T)
if(A.simulated)
return FALSE
return TRUE

/proc/screen_loc2turf(scr_loc, turf/origin)
var/tX = splittext(scr_loc, ",")
var/tY = splittext(tX[2], ":")
Expand Down
3 changes: 3 additions & 0 deletions code/datums/components/scope.dm
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
))

/datum/component/scope/process()
if(!tracker)
STOP_PROCESSING(SSprojectiles, src)
return
var/mob/user_mob = tracker.owner
var/client/user_client = user_mob.client
if(!user_client)
Expand Down
2 changes: 1 addition & 1 deletion code/datums/status_effects/neutral.dm
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@
for(var/mob/living/L in range(10, our_scope.given_turf))
if(locks >= LWAP_LOCK_CAP)
return
if(L == owner || L.stat == DEAD || isslime(L) || ismonkeybasic(L) || L.invisibility > owner.see_invisible) //xenobio moment
if(L == owner || L.stat == DEAD || isslime(L) || ismonkeybasic(L) || L.invisibility > owner.see_invisible || isLivingSSD(L)) //xenobio moment
continue
new /obj/effect/temp_visual/single_user/lwap_ping(owner.loc, owner, L)
locks++
Expand Down
4 changes: 4 additions & 0 deletions code/game/gamemodes/miniantags/morph/morph.dm
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@
failed_ambush()
to_chat(src, "<span class='warning'>You moved out of your ambush spot!</span>")

/mob/living/simple_animal/hostile/morph/add_ventcrawl()
. = ..()
on_move()

/mob/living/simple_animal/hostile/morph/death(gibbed)
. = ..()
add_to_all_human_data_huds()
Expand Down
108 changes: 92 additions & 16 deletions code/game/machinery/vendors/custom_vendors.dm
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#define INSERT_FAIL 0
#define INSERT_DONE 1
#define INSERT_NEEDS_INPUT 2

/obj/machinery/economy/vending/custom
name = "\improper CrewVend 3000"
refill_canister = null
Expand All @@ -17,6 +21,9 @@
return linked_pos?.linked_account || ..()

/obj/machinery/economy/vending/custom/item_interaction(mob/living/user, obj/item/used, list/modifiers)
if(user.a_intent == INTENT_HARM)
return ..()

if(istype(used, /obj/item/eftpos))
visible_message("<span class='notice'>[src] beeps as [user] links it to [used].</span>", "<span class='notice'>You hear something beep.</span>")
if(!isnull(linked_pos))
Expand All @@ -30,9 +37,25 @@
return ITEM_INTERACT_COMPLETE
else if(locked())
return ..()
if(!user.canUnEquip(used, FALSE))

try_add_stock(user, used)
return ITEM_INTERACT_COMPLETE

/// Tries to add something to the vendor. can_wait returns INSERT_NEEDS_INPUT if it would wait for user input, quiet suppresses success messages, and bag is used when the item is being transferred from a storage item.
/obj/machinery/economy/vending/custom/proc/try_add_stock(mob/living/user, obj/item/used, can_wait = TRUE, quiet = FALSE, obj/item/storage/bag = null)
if(isnull(bag) && !user.canUnEquip(used, FALSE))
to_chat(user, "<span class='warning'>\The [used] is stuck to your hand!</span>")
return ITEM_INTERACT_COMPLETE
return INSERT_FAIL
else if(bag)
if(!Adjacent(user))
to_chat(user, "<span class='warning'>You can't reach [src] from here!</span>")
return INSERT_FAIL
if(!user.is_holding(bag))
to_chat(user, "<span class='warning'>\The [bag] isn't in your hand anymore!</span>")
return INSERT_FAIL
if(used.loc != bag)
to_chat(user, "<span class='warning'>\The [used] isn't in [bag] anymore!</span>")
return INSERT_FAIL

for(var/datum/data/vending_product/physical/record in physical_product_records)
if(record.get_amount_left() == 0)
Expand All @@ -42,33 +65,82 @@
var/obj/item/existing = record.items[1]
if(existing.should_stack_with(used))
record.items += used
user.unequip(used)
if(isnull(bag))
user.unequip(used)
else
bag.remove_from_storage(used)
used.moveToNullspace()
user.visible_message("<span class='notice'>[user] puts [used] into [src].</span>", "<span class='notice>'You put [used] into [src].</span>")
return ITEM_INTERACT_COMPLETE
if(!quiet)
user.visible_message("<span class='notice'>[user] puts [used] into [src].</span>", "<span class='notice>'You put [used] into [src].</span>")
return INSERT_DONE

if(!can_wait)
return INSERT_NEEDS_INPUT

var/price = tgui_input_number(user, "How much do you want to sell [used] for?")
if(!isnum(price))
return ITEM_INTERACT_COMPLETE
return INSERT_FAIL
if(!Adjacent(user))
to_chat(user, "<span class='warning'>You can't reach [src] from here!</span>")
return ITEM_INTERACT_COMPLETE
if(!user.is_holding(used))
to_chat(user, "<span class='warning'>\The [used] isn't in your hand anymore!</span>")
return ITEM_INTERACT_COMPLETE
if(!user.canUnEquip(used, FALSE))
to_chat(user, "<span class='warning'>\The [used] is stuck to your hand!</span>")
return ITEM_INTERACT_COMPLETE
return INSERT_FAIL
if(isnull(bag))
if(!user.is_holding(used))
to_chat(user, "<span class='warning'>\The [used] isn't in your hand anymore!</span>")
return INSERT_FAIL
if(!user.canUnEquip(used, FALSE))
to_chat(user, "<span class='warning'>\The [used] is stuck to your hand!</span>")
return INSERT_FAIL
else
if(!user.is_holding(bag))
to_chat(user, "<span class='warning'>\The [bag] isn't in your hand anymore!</span>")
return INSERT_FAIL
if(used.loc != bag)
to_chat(user, "<span class='warning'>\The [used] isn't in [bag] anymore!</span>")
return INSERT_FAIL

var/datum/data/vending_product/physical/record = new(used.name, used.icon, used.icon_state)
record.items += used
record.price = price
physical_product_records += record
SStgui.update_uis(src, TRUE)
user.unequip(used)
if(isnull(bag))
user.unequip(used)
else
bag.remove_from_storage(used)
used.moveToNullspace()
user.visible_message("[user] puts [used] into [src].", "You put [used] into [src].")
return ITEM_INTERACT_COMPLETE
if(!quiet)
user.visible_message("<span class='notice'>[user] puts [used] into [src].</span>", "<span class='notice'>You put [used] into [src].</span>")
return INSERT_DONE

/obj/machinery/economy/vending/custom/MouseDrop_T(atom/dragged, mob/user, params)
if(!istype(dragged, /obj/item/storage))
return ..()

var/obj/item/storage/bag = dragged
var/inserted = FALSE
for(var/obj/item/thing in bag.contents.Copy())
var/result = try_add_stock(user, thing, can_wait = FALSE, quiet = TRUE, bag = bag)
if(result == INSERT_FAIL)
break
if(result == INSERT_DONE)
inserted = TRUE
continue

// result == INSERT_NEEDS_INPUT
if(inserted)
user.visible_message("<span class='notice'>[user] transfers some things from [bag] into [src].</span>", "<span class='notice'>You transfer some things from [bag] into [src].</span>")
// We've reported on our insertions so far, don't repeat it.
inserted = FALSE

// Try again, this time expecting it to wait.
result = try_add_stock(user, thing, bag = bag)
if(result == INSERT_FAIL)
break

if(inserted)
user.visible_message("<span class='notice'>[user] transfers everything from [bag] into [src].</span>", "<span class='notice'>You transfer everything from [bag] into [src].</span>")

return TRUE

/obj/machinery/economy/vending/custom/crowbar_act(mob/user, obj/item/I)
if(!isnull(linked_pos) && linked_pos.transaction_locked)
Expand All @@ -82,3 +154,7 @@
physical_product_records -= R
physical_hidden_records -= R
SStgui.update_uis(src, TRUE)

#undef INSERT_FAIL
#undef INSERT_DONE
#undef INSERT_NEEDS_INPUT
2 changes: 1 addition & 1 deletion code/game/objects/items/weapons/cards_ids.dm
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@

/obj/item/card/emag/magic_key/interact_with_atom(atom/target, mob/living/user, list/modifiers)
if(!isairlock(target))
return ITEM_INTERACT_COMPLETE
return NONE
var/obj/machinery/door/D = target
D.locked = FALSE
D.update_icon()
Expand Down
7 changes: 7 additions & 0 deletions code/game/objects/items/weapons/dice.dm
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,13 @@
qdel(I)
if(5)
//Monkeying
if(ismachineperson(user))
playsound(get_turf(user), 'sound/machines/ding.ogg', 100, 1)
var/obj/fresh_toast = new /obj/item/food/toast(get_turf(user))
fresh_toast.desc += " It came out of [user]!"
to_chat(user, "<span class='userdanger'>Your internal structure is getting really toasty!</span>")
user.gib()
return
T.visible_message("<span class='userdanger'>[user] transforms into a monkey!</span>")
user.monkeyize()
if(6)
Expand Down
21 changes: 18 additions & 3 deletions code/modules/antagonists/changeling/powers/mutations.dm
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,20 @@
throw_range = 0
throw_speed = 0
var/datum/action/changeling/weapon/parent_action
/// Used for deleting gun after hitting something
var/hit_something = FALSE
/// True if we're shooting our shot -- used to track shooting to prevent deleting mid shot
var/shooting_right_now = FALSE

/obj/item/gun/magic/tentacle/process_fire(atom/target, mob/living/user, message, params, zone_override, bonus_spread)
shooting_right_now = TRUE
. = ..()
shooting_right_now = FALSE
check_should_delete()

/obj/item/gun/magic/tentacle/proc/check_should_delete()
if(!shooting_right_now && hit_something)
qdel(src)

/obj/item/gun/magic/tentacle/customised_abstract_text(mob/living/carbon/owner)
return "<span class='warning'>[owner.p_their(TRUE)] [owner.l_hand == src ? "left arm" : "right arm"] has been turned into a grotesque tentacle.</span>"
Expand Down Expand Up @@ -294,7 +308,8 @@

/obj/item/projectile/tentacle/on_hit(atom/target, blocked = 0)
var/mob/living/carbon/human/H = firer
qdel(source.gun)
source.gun.hit_something = TRUE
source.gun.check_should_delete()
if(blocked >= 100)
return FALSE
if(isitem(target))
Expand Down Expand Up @@ -353,8 +368,8 @@
add_attack_logs(H, C, "imobilised with a changeling tentacle")
if(!iscarbon(H))
return TRUE
var/obj/item/restraints/legcuffs/beartrap/changeling/B = new(H.loc)
B.Crossed(C)
var/obj/item/restraints/legcuffs/beartrap/changeling/B = new(get_turf(L))
B.on_atom_entered(C, L)
return TRUE

if(INTENT_HARM)
Expand Down
30 changes: 20 additions & 10 deletions code/modules/events/infestation.dm
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,27 @@

/datum/event/infestation/start()
var/list/turf/simulated/floor/turfs = list()
spawn_area_type = pick(spawn_areas)
for(var/areapath in typesof(spawn_area_type))
var/area/A = locate(areapath)
if(!A)
log_debug("Failed to locate area for infestation event!")
kill()
return
for(var/turf/simulated/floor/F in A.contents)
if(turf_clear(F))
turfs += F
// shuffle in place so we don't have do that dance where we make a copy of
// the list, then pick and take, then do some conditional logic to make sure
// there's still areas to choose from, etc etc, it's a small list, it's cheap
shuffle_inplace(spawn_areas)
for(var/spawn_area in spawn_areas)
for(var/area_type in typesof(spawn_area))
var/area/destination = locate(area_type)
if(!destination)
continue
for(var/turf/simulated/floor/F in destination.contents)
if(!is_blocked_turf(F))
turfs += F
if(length(turfs))
spawn_area_type = area_type
spawn_on_turfs(turfs)
return

log_debug("Failed to locate area for infestation event!")
kill()

/datum/event/infestation/proc/spawn_on_turfs(list/turfs)
var/list/spawn_types = list()
var/max_number
vermin = rand(0, 2)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/mob/living/carbon/human/human_defense.dm
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,7 @@ emp_act
return TRUE

/mob/living/carbon/human/projectile_hit_check(obj/item/projectile/P)
return (HAS_TRAIT(src, TRAIT_FLOORED) || HAS_TRAIT(src, TRAIT_NOKNOCKDOWNSLOWDOWN)) && !density && !(P.always_hit_living_nondense && (stat != DEAD)) // hit mobs that are intentionally lying down to prevent combat crawling.
return (HAS_TRAIT(src, TRAIT_FLOORED) || HAS_TRAIT(src, TRAIT_NOKNOCKDOWNSLOWDOWN)) && !density && !(P.always_hit_living_nondense && (stat != DEAD) && !isLivingSSD(src)) // hit mobs that are intentionally lying down to prevent combat crawling.

/mob/living/carbon/human/canBeHandcuffed()
return has_left_hand() || has_right_hand()
2 changes: 1 addition & 1 deletion code/modules/mob/mob_movement.dm
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
return (!mover.density || !density || horizontal)

/mob/proc/projectile_hit_check(obj/item/projectile/P)
return !(P.always_hit_living_nondense && (stat != DEAD)) && !density
return !(P.always_hit_living_nondense && (stat != DEAD) && !isLivingSSD(src)) && !density

/client/verb/toggle_throw_mode()
set hidden = 1
Expand Down
6 changes: 6 additions & 0 deletions code/modules/paperwork/photography.dm
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@
var/icon_off = "camera_off"
var/size = 3
var/see_ghosts = FALSE //for the spoop of it
/// Cult portals and unconcealed runes have a minor form of invisibility
var/see_cult = TRUE
var/current_photo_num = 1
var/digital = FALSE
/// Should camera light up the scene
Expand Down Expand Up @@ -291,6 +293,10 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor
atoms.Add(A)
continue

// AI can't see unconcealed runes or cult portals
if(A.invisibility == INVISIBILITY_RUNES && see_cult)
atoms.Add(A)
continue
if(A.invisibility)
if(see_ghosts && isobserver(A))
var/mob/dead/observer/O = A
Expand Down
1 change: 1 addition & 0 deletions code/modules/paperwork/silicon_photography.dm
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
/// camera AI can take pictures with
/obj/item/camera/siliconcam/ai_camera
name = "AI photo camera"
see_cult = FALSE

/// camera cyborgs can take pictures with
/obj/item/camera/siliconcam/robot_camera
Expand Down
Loading