holiday and quirk support

This commit is contained in:
SandPoot
2024-04-10 22:51:25 -03:00
parent c16af5deed
commit 984d7ffe9a
6 changed files with 312 additions and 88 deletions

View File

@@ -1,20 +1,33 @@
//every quirk in this folder should be coded around being applied on spawn
//these are NOT "mob quirks" like GOTTAGOFAST, but exist as a medium to apply them and other different effects
/datum/quirk
/// The name of the quirk
var/name = "Test Quirk"
/// The description of the quirk
var/desc = "This is a test quirk."
/// What the quirk is worth in preferences, zero = neutral / free
var/value = 0
var/human_only = TRUE
/// Text displayed when this quirk is assigned to a mob (and not transferred)
var/gain_text
/// Text displayed when this quirk is removed from a mob (and not transferred)
var/lose_text
var/medical_record_text //This text will appear on medical records for the trait. Not yet implemented
var/antag_removal_text // Text will be given to the quirk holder if they get an antag that has it blacklisted.
/// This text will appear on medical records for the trait.
var/medical_record_text
/// Text will be given to the quirk holder if they get an antag that has it blacklisted.
var/antag_removal_text
var/mood_quirk = FALSE //if true, this quirk affects mood and is unavailable if moodlets are disabled
var/mob_trait //if applicable, apply and remove this mob trait
/// if applicable, apply and remove this mob trait
var/mob_trait
/// should we immediately call on_spawn or add a timer to trigger
var/on_spawn_immediate = TRUE
/// Reference to the mob currently tied to this quirk datum. Quirks are not singletons.
var/mob/living/quirk_holder
var/processing_quirk = FALSE
/// A lazylist of items people can receive from mail who have this quirk enabled
/// The base weight for the each quirk's mail goodies list to be selected is 5
/// then the item selected is determined by pick(selected_quirk.mail_goodies)
var/list/mail_goodies
/datum/quirk/New(mob/living/quirk_mob, spawn_effects)
if(!quirk_mob || (human_only && !ishuman(quirk_mob)) || quirk_mob.has_quirk(type))

View File

@@ -9,6 +9,12 @@
antag_removal_text = "Your antagonistic nature has removed your blood deficiency."
medical_record_text = "Patient requires regular treatment for blood loss due to low production of blood."
/datum/quirk/blooddeficiency/add()
RegisterSignal(quirk_holder, COMSIG_SPECIES_GAIN, PROC_REF(update_mail))
var/mob/living/carbon/human/human_holder = quirk_holder
update_mail(new_species = human_holder.dna.species)
/datum/quirk/blooddeficiency/on_process()
var/mob/living/carbon/human/H = quirk_holder
if(NOBLOOD in H.dna.species.species_traits) //can't lose blood if your species doesn't have any
@@ -16,6 +22,24 @@
else
quirk_holder.blood_volume -= 0.2
/datum/quirk/blooddeficiency/proc/update_mail(datum/source, datum/species/new_species, datum/species/old_species)
SIGNAL_HANDLER
mail_goodies.Cut()
if(isnull(new_species.exotic_blood)) // && isnull(new_species.exotic_bloodtype)) // We don't really support your blood yet :(
if(NOBLOOD in new_species.inherent_traits)
return
mail_goodies += /obj/item/reagent_containers/blood/OMinus
return
for(var/obj/item/reagent_containers/blood/blood_bag as anything in typesof(/obj/item/reagent_containers/blood))
var/right_blood_type = !isnull(new_species.exotic_bloodtype) && initial(blood_bag.blood_type) == new_species.exotic_bloodtype
// var/right_blood_reagent = !isnull(new_species.exotic_blood) && initial(blood_bag.unique_blood) == new_species.exotic_blood
if(right_blood_type) // || right_blood_reagent)
mail_goodies += blood_bag
/datum/quirk/depression
name = "Depression"
desc = "You sometimes just hate life."

View File

@@ -13,8 +13,8 @@
mouse_drag_pointer = MOUSE_ACTIVE_POINTER
/// Destination tagging for the mail sorter.
var/sort_tag = 0
/// Who this mail is for and who can open it.
var/datum/weakref/recipient
/// Weak reference to who this mail is for and who can open it.
var/datum/weakref/recipient_ref
/// How many goodies this mail contains.
var/goodie_count = 1
/// Goodies which can be given to anyone. The base weight for cash is 56. For there to be a 50/50 chance of getting a department item, they need 56 weight as well.
@@ -83,8 +83,8 @@
pixel_y = stamp_offset_y + bonus_stamp_offset
)
stamp_image.appearance_flags |= RESET_COLOR
add_overlay(stamp_image)
bonus_stamp_offset -= 5
. += stamp_image
if(postmarked == TRUE)
var/image/postmark_image = image(
@@ -94,7 +94,7 @@
pixel_y = stamp_offset_y + rand(bonus_stamp_offset + 3, 1)
)
postmark_image.appearance_flags |= RESET_COLOR
add_overlay(postmark_image)
. += postmark_image
/obj/item/mail/attackby(obj/item/W, mob/user, params)
// Destination tagging
@@ -103,52 +103,106 @@
if(sort_tag != destination_tag.currTag)
var/tag = uppertext(GLOB.TAGGERLOCATIONS[destination_tag.currTag])
to_chat(user, "<span class='notice'>*[tag]*</span>")
to_chat(user, span_notice("*[tag]*"))
sort_tag = destination_tag.currTag
playsound(loc, 'sound/machines/twobeep_high.ogg', 100, TRUE)
playsound(loc, 'sound/machines/twobeep_high.ogg', vol = 100, vary = TRUE)
/obj/item/mail/multitool_act(mob/living/user, obj/item/tool)
if(user.get_inactive_held_item() == src)
balloon_alert(user, "nothing to disable!")
return TRUE
balloon_alert(user, "hold it!")
return FALSE
/obj/item/mail/attack_self(mob/user)
if(recipient && user != recipient)
to_chat(user, "<span class='notice'>You can't open somebody else's mail! That's <em>illegal</em>!</span>")
return
if(!unwrap(user))
return FALSE
return after_unwrap(user)
user.visible_message("<span class='notice'>You start to unwrap the package...</span>")
/// proc for unwrapping a mail. Goes just for an unwrapping procces, returns FALSE if it fails.
/obj/item/mail/proc/unwrap(mob/user)
if(recipient_ref)
var/datum/mind/recipient = recipient_ref.resolve()
// If the recipient's mind has gone, then anyone can open their mail
// whether a mind can actually be qdel'd is an exercise for the reader
if(recipient && recipient != user?.mind)
to_chat(user, span_notice("You can't open somebody else's mail! That's <em>illegal</em>!"))
return FALSE
balloon_alert(user, "unwrapping...")
if(!do_after(user, 1.5 SECONDS, target = user))
return
user.temporarilyRemoveItemFromInventory(src, TRUE)
if(contents.len)
user.put_in_hands(contents[1])
playsound(loc, 'sound/items/poster_ripped.ogg', 50, TRUE)
qdel(src)
return FALSE
return TRUE
/// Accepts a mob to initialize goodies for a piece of mail.
/obj/item/mail/proc/initialize_for_recipient(mob/new_recipient)
recipient = new_recipient
name = "[initial(name)] for [new_recipient.real_name] ([new_recipient.job])"
// proc that goes after unwrapping a mail.
/obj/item/mail/proc/after_unwrap(mob/user)
user.temporarilyRemoveItemFromInventory(src, force = TRUE)
for(var/obj/stuff as anything in contents) // Mail and envelope actually can have more than 1 item.
if(isitem(stuff))
user.put_in_hands(stuff)
else
stuff.forceMove(drop_location())
playsound(loc, 'sound/items/poster_ripped.ogg', vol = 50, vary = TRUE)
qdel(src)
return TRUE
/obj/item/mail/examine_more(mob/user)
. = ..()
if(!postmarked)
. += span_info("This mail has no postmarking of any sort...")
else
. += span_notice("<i>You notice the postmarking on the front of the mail...</i>")
var/datum/mind/recipient = recipient_ref.resolve()
if(recipient)
. += span_info("[postmarked ? "Certified NT" : "Uncertfieid"] mail for [recipient].")
else if(postmarked)
. += span_info("Certified mail for [GLOB.station_name].")
else
. += span_info("This is a dead letter mail with no recipient.")
. += span_info("Distribute by hand or via destination tagger using the certified NT disposal system.")
/// Accepts a mind to initialize goodies for a piece of mail.
/obj/item/mail/proc/initialize_for_recipient(datum/mind/recipient)
name = "[initial(name)] for [recipient.name] ([recipient.assigned_role])"
recipient_ref = WEAKREF(recipient)
var/mob/living/body = recipient.current
var/list/goodies = generic_goodies
var/datum/job/this_job = SSjob.name_occupations[new_recipient.job]
var/datum/job/this_job = SSjob.name_occupations[recipient.assigned_role]
var/is_mail_restricted = FALSE // certain roles and jobs (prisoner) do not receive generic gifts
if(this_job)
if(this_job.paycheck_department && department_colors[this_job.paycheck_department])
color = department_colors[this_job.paycheck_department]
var/list/job_goodies = this_job.get_mail_goodies()
is_mail_restricted = this_job.exclusive_mail_goodies
if(LAZYLEN(job_goodies))
// certain roles and jobs (prisoner) do not receive generic gifts.
if(this_job.exclusive_mail_goodies)
if(is_mail_restricted)
goodies = job_goodies
else
goodies += job_goodies
for(var/iterator = 0, iterator < goodie_count, iterator++)
if(!is_mail_restricted)
// the weighted list is 50 (generic items) + 50 (job items)
// every quirk adds 5 to the final weighted list (regardless the number of items or weights in the quirk list)
// 5% is not too high or low so that stacking multiple quirks doesn't tilt the weighted list too much
for(var/datum/quirk/quirk as anything in body.roundstart_quirks)
if(LAZYLEN(quirk.mail_goodies))
var/quirk_goodie = pick(quirk.mail_goodies)
goodies[quirk_goodie] = 5
// A little boost for the special times!
for(var/datum/holiday/holiday as anything in SSevents.holidays)
if(LAZYLEN(holiday.mail_goodies))
var/holiday_goodie = pick(holiday.mail_goodies)
goodies[holiday_goodie] = holiday.mail_goodies[holiday_goodie]
for(var/iterator in 1 to goodie_count)
var/target_good = pickweight(goodies)
if(ispath(target_good, /datum/reagent))
var/obj/item/reagent_containers/target_container = new /obj/item/reagent_containers/glass/bottle(src)
target_container.reagents.add_reagent(target_good, target_container.volume)
target_container.name = "[target_container.reagents.reagent_list[1].name] bottle"
new_recipient.log_message("[key_name(new_recipient)] received reagent container [target_container.name] in the mail ([target_good])", LOG_GAME)
else
var/atom/movable/target_atom = new target_good(src)
new_recipient.log_message("[key_name(new_recipient)] received [target_atom.name] in the mail ([target_good])", LOG_GAME)
var/atom/movable/target_atom = new target_good(src)
body.log_message("received [target_atom.name] in the mail ([target_good])", LOG_GAME)
return TRUE
@@ -190,41 +244,87 @@
name = "mail crate"
desc = "A certified post crate from CentCom."
icon_state = "mail"
base_icon_state = "mail"
///if it'll show the nt mark on the crate
var/postmarked = TRUE
/obj/structure/closet/crate/mail/update_icon_state()
. = ..()
if(opened)
icon_state = "[base_icon_state]open"
if(locate(/obj/item/mail) in src)
icon_state = base_icon_state
else
icon_state = "[base_icon_state]sealed"
/obj/structure/closet/crate/mail/update_overlays()
. = ..()
if(postmarked)
. += "mail_nt"
/// Fills this mail crate with N pieces of mail, where N is the lower of the amount var passed, and the maximum capacity of this crate. If N is larger than the number of alive human players, the excess will be junkmail.
/obj/structure/closet/crate/mail/proc/populate(amount)
var/mail_count = min(amount, storage_capacity)
// Fills the
var/list/mail_recipients = list()
for(var/mob/living/carbon/human/human in GLOB.player_list)
if(human.stat == DEAD || !human.mind)
continue
// Skip wizards, nuke ops, cyborgs; Centcom does not send them mail
if(!(human.mind.assigned_role in get_all_jobs()))
continue
mail_recipients += human.mind
for(var/i in 1 to mail_count)
var/obj/item/mail/new_mail
if(prob(FULL_CRATE_LETTER_ODDS))
new_mail = new /obj/item/mail(src)
else
new_mail = new /obj/item/mail/envelope(src)
var/datum/mind/recipient = pick_n_take(mail_recipients)
if(recipient)
new_mail.initialize_for_recipient(recipient)
else
new_mail.junk_mail()
update_icon()
/// Crate for mail that automatically depletes the economy subsystem's pending mail counter.
/obj/structure/closet/crate/mail/economy/Initialize(mapload)
. = ..()
populate(SSeconomy.mail_waiting)
SSeconomy.mail_waiting = 0
/// Crate for mail that automatically generates a lot of mail. Usually only normal mail, but on lowpop it may end up just being junk.
/obj/structure/closet/crate/mail/full
name = "brimming mail crate"
desc = "A certified post crate from CentCom. Looks stuffed to the gills."
/obj/structure/closet/crate/mail/update_icon_state()
/obj/structure/closet/crate/mail/full/Initialize(mapload)
. = ..()
if(opened)
icon_state = "[initial(icon_state)]open"
if(locate(/obj/item/mail) in src)
icon_state = initial(icon_state)
else
icon_state = "[initial(icon_state)]sealed"
populate(INFINITY)
/obj/structure/closet/crate/mail/full/Initialize()
. = ..()
var/list/mail_recipients = list()
for(var/mob/living/carbon/human/alive in GLOB.player_list)
if(alive.stat != DEAD)
mail_recipients += alive
for(var/iterator in 1 to storage_capacity)
var/obj/item/mail/new_mail
if(prob(FULL_CRATE_LETTER_ODDS))
new_mail = new /obj/item/mail(src)
else
new_mail = new /obj/item/mail/envelope(src)
var/mob/living/carbon/human/mail_to
mail_to = pick(mail_recipients)
if(mail_to)
new_mail.initialize_for_recipient(mail_to)
mail_recipients -= mail_to //Once picked, the mail crate will need a new recipient.
else
new_mail.junk_mail()
///Used in the mail strike shuttle loan event
/* /obj/structure/closet/crate/mail/full/mail_strike
desc = "A post crate from somewhere else. It has no NT logo on it."
postmarked = FALSE
/obj/structure/closet/crate/mail/full/mail_strike/populate(amount)
var/strike_mail_to_spawn = rand(1, storage_capacity-1)
for(var/i in 1 to strike_mail_to_spawn)
if(prob(95))
new /obj/item/mail/mail_strike(src)
else
new /obj/item/mail/traitor/mail_strike(src)
return ..(storage_capacity - strike_mail_to_spawn) */
/// Opened mail crate
/obj/structure/closet/crate/mail/preopen
opened = TRUE
icon_state = "mailopen"
/// Mailbag.
/obj/item/storage/bag/mail
@@ -274,3 +374,106 @@
/obj/item/paper/fluff/junkmail_generic/Initialize()
. = ..()
info = pick(GLOB.junkmail_messages)
/* Maybe some other time
/obj/item/mail/traitor
var/armed = FALSE
var/datum/weakref/made_by_ref
/// Cached information about who made it for logging purposes
var/made_by_cached_name
/// Cached information about who made it for logging purposes
var/made_by_cached_ckey
goodie_count = 0
/obj/item/mail/traitor/envelope
name = "envelope"
icon_state = "mail_large"
stamp_max = 2
stamp_offset_y = 5
/obj/item/mail/traitor/after_unwrap(mob/user)
user.temporarilyRemoveItemFromInventory(src, force = TRUE)
playsound(loc, 'sound/items/poster_ripped.ogg', vol = 50, vary = TRUE)
for(var/obj/item/stuff as anything in contents) // Mail and envelope actually can have more than 1 item.
if(user.put_in_hands(stuff) && armed)
var/whomst = made_by_cached_name ? "[made_by_cached_name] ([made_by_cached_ckey])" : "no one in particular"
log_bomber(user, "opened armed mail made by [whomst], activating", stuff)
INVOKE_ASYNC(stuff, TYPE_PROC_REF(/obj/item, attack_self), user)
qdel(src)
return TRUE
/obj/item/mail/traitor/multitool_act(mob/living/user, obj/item/tool)
if(armed == FALSE || user.get_inactive_held_item() != src)
return ..()
if(IS_WEAKREF_OF(user.mind, made_by_ref))
balloon_alert(user, "disarming trap...")
if(!do_after(user, 2 SECONDS, target = src))
return FALSE
balloon_alert(user, "disarmed")
playsound(src, 'sound/machines/defib_ready.ogg', vol = 100, vary = TRUE)
armed = FALSE
return TRUE
else
balloon_alert(user, "tinkering with something...")
if(!do_after(user, 2 SECONDS, target = src))
after_unwrap(user)
return FALSE
if(prob(50))
balloon_alert(user, "disarmed something...?")
playsound(src, 'sound/machines/defib_ready.ogg', vol = 100, vary = TRUE)
armed = FALSE
return TRUE
else
after_unwrap(user)
return TRUE
///Generic mail used in the mail strike shuttle loan event
/obj/item/mail/mail_strike
name = "dead mail"
desc = "An unmarked parcel of unknown origins, effectively undeliverable."
postmarked = FALSE
generic_goodies = list(
/obj/effect/spawner/random/entertainment/money_medium = 2,
/obj/effect/spawner/random/contraband = 2,
/obj/effect/spawner/random/entertainment/money_large = 1,
/obj/effect/spawner/random/entertainment/coin = 1,
/obj/effect/spawner/random/food_or_drink/any_snack_or_beverage = 1,
/obj/effect/spawner/random/entertainment/drugs = 1,
/obj/effect/spawner/random/contraband/grenades = 1,
)
/obj/item/mail/mail_strike/Initialize(mapload)
if(prob(35))
stamped = FALSE
if(prob(35))
name = "dead envelope"
icon_state = "mail_large"
goodie_count = 2
stamp_max = 2
stamp_offset_y = 5
. = ..()
color = pick(COLOR_SILVER, COLOR_DARK, COLOR_DRIED_TAN, COLOR_ORANGE_BROWN, COLOR_BROWN, COLOR_SYNDIE_RED)
for(var/goodie in 1 to goodie_count)
var/target_good = pick_weight(generic_goodies)
new target_good(src)
///Also found in the mail strike shuttle loan. It contains a random grenade that'll be triggered when unwrapped
/obj/item/mail/traitor/mail_strike
name = "dead mail"
desc = "An unmarked parcel of unknown origins, effectively undeliverable."
postmarked = FALSE
/obj/item/mail/traitor/mail_strike/Initialize(mapload)
if(prob(35))
stamped = FALSE
if(prob(35))
name = "dead envelope"
icon_state = "mail_large"
goodie_count = 2
stamp_max = 2
stamp_offset_y = 5
. = ..()
color = pick(COLOR_SILVER, COLOR_DARK, COLOR_DRIED_TAN, COLOR_ORANGE_BROWN, COLOR_BROWN, COLOR_SYNDIE_RED)
new /obj/effect/spawner/random/contraband/grenades/dangerous(src)
*/

View File

@@ -12,6 +12,9 @@
var/year_offset = 0
var/obj/item/drone_hat //If this is defined, drones without a default hat will spawn with this one during the holiday; check drones_as_items.dm to see this used
// Special things to be given during this!
var/list/mail_goodies
// This proc gets run before the game starts when the holiday is activated. Do festive shit here.
/datum/holiday/proc/celebrate()
return
@@ -398,6 +401,11 @@
end_day = 2
end_month = NOVEMBER
mail_goodies = list(
/obj/item/reagent_containers/food/snacks/lollipop = 10,
/obj/item/reagent_containers/food/snacks/chocolatebar = 10
)
/datum/holiday/halloween/greet()
return "Have a spooky Halloween!"

View File

@@ -304,40 +304,16 @@ GLOBAL_LIST_INIT(cargo_shuttle_leave_behind_typecache, typecacheof(list(
//Early return if there's no mail waiting to prevent taking up a slot.
if(!SSeconomy.mail_waiting)
return
//spawn crate
var/list/empty_turfs = list()
for(var/place as anything in shuttle_areas)
var/area/shuttle/shuttle_area = place
for(var/area/shuttle/shuttle_area as anything in shuttle_areas)
for(var/turf/open/floor/shuttle_floor in shuttle_area)
if(is_blocked_turf(shuttle_floor))
continue
empty_turfs += shuttle_floor
var/obj/structure/closet/crate/mail/mailcrate = new(pick(empty_turfs))
//collect recipients
var/list/mail_recipients = list()
for(var/mob/living/carbon/human/player_human in GLOB.player_list)
if(player_human.stat != DEAD)
mail_recipients += player_human
//Creates mail for all the mail waiting to arrive, if there's nobody to recieve it it's just junkmail.
for(var/mail_iterator in 1 to SSeconomy.mail_waiting)
var/obj/item/mail/new_mail
if(prob(FULL_CRATE_LETTER_ODDS))
new_mail = new /obj/item/mail(mailcrate)
else
new_mail = new /obj/item/mail/envelope(mailcrate)
var/mob/living/carbon/human/mail_to
if(mail_recipients.len)
mail_to = pick(mail_recipients)
new_mail.initialize_for_recipient(mail_to)
mail_recipients -= mail_to
else
new_mail.junk_mail()
if(new_mail)
SSeconomy.mail_waiting += 1
mailcrate.update_icon()
return mailcrate
new /obj/structure/closet/crate/mail/economy(pick(empty_turfs))
#undef GOODY_FREE_SHIPPING_MAX
#undef CRATE_TAX

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB