diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm
index 2784f9d782b..67264694034 100644
--- a/code/__DEFINES/logging.dm
+++ b/code/__DEFINES/logging.dm
@@ -16,6 +16,7 @@
#define INVESTIGATE_EXONET "exonet"
#define INVESTIGATE_NANITES "nanites"
#define INVESTIGATE_CIRCUIT "circuit"
+#define INVESTIGATE_PRESENTS "presents"
// Logging types for log_message()
#define LOG_ATTACK (1 << 0)
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 962634f35df..2eb7107601a 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -57,6 +57,8 @@
#define TRAIT_PARALYSIS_R_ARM "para-r-arm"
#define TRAIT_PARALYSIS_L_LEG "para-l-leg"
#define TRAIT_PARALYSIS_R_LEG "para-r-leg"
+#define TRAIT_CANNOT_OPEN_PRESENTS "cannot-open-presents"
+#define TRAIT_PRESENT_VISION "present-vision"
#define TRAIT_DISK_VERIFIER "disk-verifier"
#define TRAIT_NOMOBSWAP "no-mob-swap"
#define TRAIT_XRAY_VISION "xray_vision"
@@ -108,6 +110,7 @@
#define GENETICS_SPELL "genetics_spell"
#define EYES_COVERED "eyes_covered"
#define CULT_EYES "cult_eyes"
+#define TRAIT_SANTA "santa"
#define SCRYING_ORB "scrying-orb"
#define ABDUCTOR_ANTAGONIST "abductor-antagonist"
#define NUKEOP_TRAIT "nuke-op"
diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm
index 35f548261bf..20554d476f8 100644
--- a/code/_globalvars/lists/flavor_misc.dm
+++ b/code/_globalvars/lists/flavor_misc.dm
@@ -36,7 +36,7 @@ GLOBAL_LIST_EMPTY(wings_open_list)
GLOBAL_LIST_EMPTY(r_wings_list)
GLOBAL_LIST_EMPTY(moth_wings_list)
GLOBAL_LIST_EMPTY(caps_list)
-
+
GLOBAL_LIST_INIT(color_list_ethereal, list("F Class(Green)" = "97ee63", "F2 Class (Light Green)" = "00fa9a", "F3 Class (Dark Green)" = "37835b", "M Class (Red)" = "9c3030", "M1 Class (Purple)" = "ee82ee", "G Class (Yellow)" = "fbdf56", "O Class (Blue)" = "3399ff", "A Class (Cyan)" = "00ffff"))
GLOBAL_LIST_INIT(ghost_forms_with_directions_list, list("ghost")) //stores the ghost forms that support directional sprites
diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index 44741758cf2..3400079d3d9 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -38,6 +38,7 @@ GLOBAL_LIST_EMPTY(latejoin_ai_cores)
GLOBAL_LIST_EMPTY(mob_config_movespeed_type_lookup)
+
/proc/update_config_movespeed_type_lookup(update_mobs = TRUE)
var/list/mob_types = list()
var/list/entry_value = CONFIG_GET(keyed_list/multiplicative_movespeed)
diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm
index 862f0f12c3c..64d1fe4f27a 100644
--- a/code/datums/components/storage/storage.dm
+++ b/code/datums/components/storage/storage.dm
@@ -588,7 +588,8 @@
to_chat(M, "[IP] cannot hold [I] as it's a storage item of the same size!")
return FALSE //To prevent the stacking of same sized storage items.
if(I.item_flags & NODROP) //SHOULD be handled in unEquip, but better safe than sorry.
- to_chat(M, "\the [I] is stuck to your hand, you can't put it in \the [host]!")
+ if(!stop_messages)
+ to_chat(M, "\the [I] is stuck to your hand, you can't put it in \the [host]!")
return FALSE
var/datum/component/storage/concrete/master = master()
if(!istype(master))
@@ -599,7 +600,7 @@
return FALSE
//This proc handles items being inserted. It does not perform any checks of whether an item can or can't be inserted. That's done by can_be_inserted()
-//The stop_warning parameter will stop the insertion message from being displayed. It is intended for cases where you are inserting multiple items at once,
+//The prevent_warning parameter will stop the insertion message from being displayed. It is intended for cases where you are inserting multiple items at once,
//such as when picking up all the items on a tile with one click.
/datum/component/storage/proc/handle_item_insertion(obj/item/I, prevent_warning = FALSE, mob/M, datum/component/storage/remote)
var/atom/parent = src.parent
diff --git a/code/datums/outfit.dm b/code/datums/outfit.dm
index 1f5c28d3c2a..68165af956b 100755
--- a/code/datums/outfit.dm
+++ b/code/datums/outfit.dm
@@ -21,6 +21,7 @@
var/l_hand = null
var/internals_slot = null //ID of slot containing a gas tank
var/list/backpack_contents = null // In the list(path=count,otherpath=count) format
+ var/box // Internals box. Will be inserted at the start of backpack_contents
var/list/implants = null
var/accessory = null
@@ -83,6 +84,13 @@
H.equip_to_slot_or_del(new l_pocket(H),SLOT_L_STORE)
if(r_pocket)
H.equip_to_slot_or_del(new r_pocket(H),SLOT_R_STORE)
+
+ if(box)
+ if(!backpack_contents)
+ backpack_contents = list()
+ backpack_contents.Insert(1, box)
+ backpack_contents[box] = 1
+
if(backpack_contents)
for(var/path in backpack_contents)
var/number = backpack_contents[path]
diff --git a/code/game/objects/items/gift.dm b/code/game/objects/items/gift.dm
index f97b3f99d6f..ab728949abb 100644
--- a/code/game/objects/items/gift.dm
+++ b/code/game/objects/items/gift.dm
@@ -18,25 +18,35 @@ GLOBAL_LIST_EMPTY(possible_gifts)
item_state = "gift"
resistance_flags = FLAMMABLE
-/obj/item/a_gift/New()
- ..()
+ var/obj/item/contains_type
+
+/obj/item/a_gift/Initialize()
+ . = ..()
pixel_x = rand(-10,10)
pixel_y = rand(-10,10)
icon_state = "giftdeliverypackage[rand(1,5)]"
+ contains_type = get_gift_type()
+
/obj/item/a_gift/suicide_act(mob/user)
user.visible_message("[user] peeks inside [src] and cries [user.p_them()]self to death! It looks like [user.p_they()] [user.p_were()] on the naughty list...")
return (BRUTELOSS)
+/obj/item/a_gift/examine(mob/M)
+ . = ..()
+ if(M.has_trait(TRAIT_PRESENT_VISION) || isobserver(M))
+ to_chat(M, "It contains \a [initial(contains_type.name)].")
+
/obj/item/a_gift/attack_self(mob/M)
- if(M && M.mind && M.mind.special_role == "Santa")
+ if(M.has_trait(TRAIT_CANNOT_OPEN_PRESENTS))
to_chat(M, "You're supposed to be spreading gifts, not opening them yourself!")
return
- var/gift_type = get_gift_type()
-
qdel(src)
- var/obj/item/I = new gift_type(M)
+
+ var/obj/item/I = new contains_type(get_turf(M))
+ M.visible_message("[M] unwraps \the [src], finding \a [I] inside!")
+ I.investigate_log("([I.type]) was found in a present by [key_name(M)].", INVESTIGATE_PRESENTS)
M.put_in_hands(I)
I.add_fingerprint(M)
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index 7905217af67..5db76fd2d89 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -65,20 +65,22 @@
sleep(20)
playsound(src, "rustle", 50, 1, -5)
qdel(user)
- return
/obj/item/storage/backpack/holding/singularity_act(current_size)
var/dist = max((current_size - 2),1)
explosion(src.loc,(dist),(dist*2),(dist*4))
- return
/obj/item/storage/backpack/santabag
name = "Santa's Gift Bag"
- desc = "Space Santa uses this to deliver toys to all the nice children in space in Christmas! Wow, it's pretty big!"
+ desc = "Space Santa uses this to deliver presents to all the nice children in space in Christmas! Wow, it's pretty big!"
icon_state = "giftbag0"
item_state = "giftbag"
w_class = WEIGHT_CLASS_BULKY
+/obj/item/storage/backpack/santabag/Initialize()
+ . = ..()
+ regenerate_presents()
+
/obj/item/storage/backpack/santabag/ComponentInitialize()
. = ..()
GET_COMPONENT(STR, /datum/component/storage)
@@ -89,6 +91,22 @@
user.visible_message("[user] places [src] over [user.p_their()] head and pulls it tight! It looks like [user.p_they()] [user.p_are()]n't in the Christmas spirit...")
return (OXYLOSS)
+/obj/item/storage/backpack/santabag/proc/regenerate_presents()
+ addtimer(CALLBACK(src, .proc/regenerate_presents), rand(30 SECONDS, 60 SECONDS))
+
+ var/mob/M = get(loc, /mob)
+ if(!istype(M))
+ return
+ if(M.has_trait(TRAIT_CANNOT_OPEN_PRESENTS))
+ GET_COMPONENT(STR, /datum/component/storage)
+ var/turf/floor = get_turf(src)
+ var/obj/item/I = new /obj/item/a_gift/anything(floor)
+ if(STR.can_be_inserted(I, stop_messages=TRUE))
+ STR.handle_item_insertion(I, prevent_warning=TRUE)
+ else
+ qdel(I)
+
+
/obj/item/storage/backpack/cultpack
name = "trophy rack"
desc = "It's useful for both carrying extra gear and proudly declaring your insanity."
diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm
index 7c73a1fd8cc..0b7d3e0bd49 100644
--- a/code/game/objects/structures/flora.dm
+++ b/code/game/objects/structures/flora.dm
@@ -63,7 +63,13 @@
icon_state = "pinepresents"
desc = "A wondrous decorated Christmas tree. It has presents!"
var/gift_type = /obj/item/a_gift/anything
- var/list/ckeys_that_took = list()
+ var/unlimited = FALSE
+ var/static/list/took_presents //shared between all xmas trees
+
+/obj/structure/flora/tree/pine/xmas/presents/Initialize()
+ . = ..()
+ if(!took_presents)
+ took_presents = list()
/obj/structure/flora/tree/pine/xmas/presents/attack_hand(mob/living/user)
. = ..()
@@ -72,14 +78,21 @@
if(!user.ckey)
return
- if(ckeys_that_took[user.ckey])
+ if(took_presents[user.ckey] && !unlimited)
to_chat(user, "There are no presents with your name on.")
return
to_chat(user, "After a bit of rummaging, you locate a gift with your name on it!")
- ckeys_that_took[user.ckey] = TRUE
+
+ if(!unlimited)
+ took_presents[user.ckey] = TRUE
+
var/obj/item/G = new gift_type(src)
user.put_in_hands(G)
+/obj/structure/flora/tree/pine/xmas/presents/unlimited
+ desc = "A wonderous decorated Christmas tree. It has a seemly endless supply of presents!"
+ unlimited = TRUE
+
/obj/structure/flora/tree/dead
icon = 'icons/obj/flora/deadtrees.dmi'
desc = "A dead tree. How it died, you know not."
diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm
index ebbef58cbb5..6285b14c686 100644
--- a/code/modules/admin/admin_investigate.dm
+++ b/code/modules/admin/admin_investigate.dm
@@ -4,7 +4,7 @@
var/F = file("[GLOB.log_directory]/[subject].html")
WRITE_FILE(F, "[time_stamp()] [REF(src)] ([x],[y],[z]) || [src] [message]
")
-/client/proc/investigate_show(subject in list("notes, memos, watchlist", INVESTIGATE_RESEARCH, INVESTIGATE_EXONET, INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS, INVESTIGATE_RADIATION, INVESTIGATE_CIRCUIT, INVESTIGATE_NANITES) )
+/client/proc/investigate_show(subject in list("notes, memos, watchlist", INVESTIGATE_RESEARCH, INVESTIGATE_EXONET, INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS, INVESTIGATE_RADIATION, INVESTIGATE_CIRCUIT, INVESTIGATE_NANITES, INVESTIGATE_PRESENTS) )
set name = "Investigate"
set category = "Admin"
if(!holder)
diff --git a/code/modules/antagonists/santa/santa.dm b/code/modules/antagonists/santa/santa.dm
index ea334ad6653..83dd08f2b1a 100644
--- a/code/modules/antagonists/santa/santa.dm
+++ b/code/modules/antagonists/santa/santa.dm
@@ -8,24 +8,24 @@
give_equipment()
give_objective()
+ owner.add_trait(TRAIT_CANNOT_OPEN_PRESENTS, TRAIT_SANTA)
+ owner.add_trait(TRAIT_PRESENT_VISION, TRAIT_SANTA)
+
/datum/antagonist/santa/greet()
. = ..()
- to_chat(owner, "You are Santa! Your objective is to bring joy to the people on this station. You can conjure more presents using a spell, and there are several presents in your bag.")
+ to_chat(owner, "You are Santa! Your objective is to bring joy to the people on this station. You have a magical bag, which generates presents as long as you have it! You can examine the presents to take a peek inside, to make sure that you give the right gift to the right person.")
/datum/antagonist/santa/proc/give_equipment()
var/mob/living/carbon/human/H = owner.current
if(istype(H))
H.equipOutfit(/datum/outfit/santa)
+ H.dna.update_dna_identity()
- owner.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/conjure/presents)
- var/obj/effect/proc_holder/spell/targeted/area_teleport/teleport/telespell = new
- telespell.clothes_req = 0 //santa robes aren't actually magical.
- owner.AddSpell(telespell) //does the station have chimneys? WHO KNOWS!
+ owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport/santa)
/datum/antagonist/santa/proc/give_objective()
var/datum/objective/santa_objective = new()
santa_objective.explanation_text = "Bring joy and presents to the station!"
- santa_objective.completed = 1 //lets cut our santas some slack.
+ santa_objective.completed = TRUE //lets cut our santas some slack.
santa_objective.owner = owner
objectives |= santa_objective
-
diff --git a/code/modules/clothing/outfits/event.dm b/code/modules/clothing/outfits/event.dm
index 347fc5c2d72..70d16f3ae96 100644
--- a/code/modules/clothing/outfits/event.dm
+++ b/code/modules/clothing/outfits/event.dm
@@ -6,11 +6,11 @@
suit = /obj/item/clothing/suit/space/santa
head = /obj/item/clothing/head/santa
back = /obj/item/storage/backpack/santabag
- mask = /obj/item/clothing/mask/breath
r_pocket = /obj/item/flashlight
gloves = /obj/item/clothing/gloves/color/red
- belt = /obj/item/tank/internals/emergency_oxygen/double
- id = /obj/item/card/id/gold
+
+ box = /obj/item/storage/box/engineer
+ backpack_contents = list(/obj/item/a_gift/anything = 5)
/datum/outfit/santa/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
if(visualsOnly)
@@ -19,12 +19,8 @@
H.mind.assigned_role = "Santa"
H.mind.special_role = "Santa"
- H.hair_style = "Long Hair"
- H.facial_hair_style = "Full Beard"
+ H.hair_style = "Long Hair 3"
+ H.facial_hair_style = "Beard (Full)"
H.hair_color = "FFF"
H.facial_hair_color = "FFF"
-
- var/obj/item/storage/backpack/bag = H.back
- var/obj/item/a_gift/gift = new(H)
- while(SEND_SIGNAL(bag, COMSIG_TRY_STORAGE_INSERT, gift, null, TRUE, FALSE))
- gift = new(H)
+ H.update_hair()
diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm
index c0da7534c59..dc9a4824ac7 100644
--- a/code/modules/events/holiday/xmas.dm
+++ b/code/modules/events/holiday/xmas.dm
@@ -63,7 +63,7 @@
christmas_tree = null
/datum/round_event_control/santa
- name = "Santa is coming to town! (Christmas)"
+ name = "Vist by Santa"
holidayID = CHRISTMAS
typepath = /datum/round_event/santa
weight = 20
diff --git a/code/modules/jobs/job_types/job.dm b/code/modules/jobs/job_types/job.dm
index 9242000636d..59ea5c00fc4 100644
--- a/code/modules/jobs/job_types/job.dm
+++ b/code/modules/jobs/job_types/job.dm
@@ -158,11 +158,11 @@
belt = /obj/item/pda
back = /obj/item/storage/backpack
shoes = /obj/item/clothing/shoes/sneakers/black
+ box = /obj/item/storage/box/survival
var/backpack = /obj/item/storage/backpack
var/satchel = /obj/item/storage/backpack/satchel
var/duffelbag = /obj/item/storage/backpack/duffelbag
- var/box = /obj/item/storage/box/survival
var/pda_slot = SLOT_BELT
@@ -183,12 +183,6 @@
else
back = backpack //Department backpack
- if(box)
- if(!backpack_contents)
- backpack_contents = list()
- backpack_contents.Insert(1, box) // Box always takes a first slot in backpack
- backpack_contents[box] = 1
-
/datum/outfit/job/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
if(visualsOnly)
return
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index f35177fe68b..8aab5376795 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -1546,7 +1546,7 @@
if(M && ishuman(M))
var/mob/living/carbon/human/H = M
H.hair_style = "Very Long Hair"
- H.facial_hair_style = "Very Long Beard"
+ H.facial_hair_style = "Beard (Very Long)"
H.update_hair()
/datum/reagent/saltpetre
diff --git a/code/modules/spells/spell_types/area_teleport.dm b/code/modules/spells/spell_types/area_teleport.dm
index 83f17969735..84e1eec2eae 100644
--- a/code/modules/spells/spell_types/area_teleport.dm
+++ b/code/modules/spells/spell_types/area_teleport.dm
@@ -8,6 +8,8 @@
var/sound1 = 'sound/weapons/zapbang.ogg'
var/sound2 = 'sound/weapons/zapbang.ogg'
+ var/say_destination = TRUE
+
/obj/effect/proc_holder/spell/targeted/area_teleport/perform(list/targets, recharge = 1,mob/living/user = usr)
var/thearea = before_cast(targets)
if(!thearea || !cast_check(1))
@@ -69,20 +71,22 @@
do_teleport(target, L, forceMove = TRUE, channel = TELEPORT_CHANNEL_MAGIC)
playsound(get_turf(user), sound2, 50,1)
- return
-
/obj/effect/proc_holder/spell/targeted/area_teleport/invocation(area/chosenarea = null,mob/user = usr)
if(!invocation_area || !chosenarea)
..()
else
+ var/words
+ if(say_destination)
+ words = "[invocation] [uppertext(chosenarea.name)]"
+ else
+ words = "[invocation]"
+
switch(invocation_type)
if("shout")
- user.say("[invocation] [uppertext(chosenarea.name)]", forced = "spell")
+ user.say(words, forced = "spell")
if(user.gender==MALE)
playsound(user.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1)
else
playsound(user.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1)
if("whisper")
- user.whisper("[invocation] [uppertext(chosenarea.name)]", forced = "spell")
-
- return
+ user.whisper(words, forced = "spell")
diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm
index 563ae028c46..2a67febb739 100644
--- a/code/modules/spells/spell_types/wizard.dm
+++ b/code/modules/spells/spell_types/wizard.dm
@@ -151,6 +151,13 @@
sound1 = 'sound/magic/teleport_diss.ogg'
sound2 = 'sound/magic/teleport_app.ogg'
+/obj/effect/proc_holder/spell/targeted/area_teleport/teleport/santa
+ name = "Santa Teleport"
+
+ invocation = "HO HO HO"
+ clothes_req = FALSE
+ say_destination = FALSE // Santa moves in mysterious ways
+
/obj/effect/proc_holder/spell/aoe_turf/conjure/timestop
name = "Stop Time"
desc = "This spell stops time for everyone except for you, allowing you to move freely while your enemies and even projectiles are frozen."