diff --git a/code/__DEFINES/achievements.dm b/code/__DEFINES/achievements.dm index c39531971c..20c68c015b 100644 --- a/code/__DEFINES/achievements.dm +++ b/code/__DEFINES/achievements.dm @@ -32,6 +32,9 @@ #define MEDAL_VOID_ASCENSION "Void" #define MEDAL_TOOLBOX_SOUL "Toolsoul" #define MEDAL_CHEM_TUT "Beginner Chemist" +#define MEDAL_HOT_DAMN "Hot Damn!" +#define MEDAL_CAYENNE_DISK "Very Important Piscis" +#define MEDAL_TRAM_SURFER "Tram Surfer" //Skill medal hub IDs #define MEDAL_LEGENDARY_MINER "Legendary Miner" @@ -106,3 +109,19 @@ // DB ID for amount of consumed maintenance pills #define MAINTENANCE_PILL_SCORE "Maintenance Pill Score" + +// DB ID for intento score +#define INTENTO_SCORE "Intento Score" + +// Tourist related achievements and scores + +//centcom grades (achievement) + +#define MEDAL_BAD_SERVICE "Bad Service" +#define MEDAL_OKAY_SERVICE "Okay Service" +#define MEDAL_GOOD_SERVICE "Good Service" + +//scores + +#define CHEF_TOURISTS_SERVED "Tourists Served As Chef" +#define BARTENDER_TOURISTS_SERVED "Tourists Served As Bartender" diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index db78144689..04c91cfb9d 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -59,3 +59,4 @@ #define COLOR_SILVER "#C0C0C0" #define COLOR_GRAY "#808080" #define COLOR_HALF_TRANSPARENT_BLACK "#0000007A" +#define COLOR_BRIGHT_BLUE "#2CB2E8" diff --git a/code/__DEFINES/cooldowns.dm b/code/__DEFINES/cooldowns.dm index 29c7a25dad..92231d3998 100644 --- a/code/__DEFINES/cooldowns.dm +++ b/code/__DEFINES/cooldowns.dm @@ -28,6 +28,11 @@ //INDEXES #define COOLDOWN_EMPLOYMENT_CABINET "employment cabinet" +//car cooldowns +#define COOLDOWN_CAR_HONK "car_honk" + +//clown car cooldowns +#define COOLDOWN_CLOWNCAR_RANDOMNESS "clown_car_randomness" //TIMER COOLDOWN MACROS diff --git a/code/__DEFINES/vehicles.dm b/code/__DEFINES/vehicles.dm index 48383546b9..1ff14f9506 100644 --- a/code/__DEFINES/vehicles.dm +++ b/code/__DEFINES/vehicles.dm @@ -1,9 +1,20 @@ -//Vehicle control flags +//Vehicle control flags. control flags describe access to actions in a vehicle. -#define VEHICLE_CONTROL_PERMISSION 1 -#define VEHICLE_CONTROL_DRIVE 2 -#define VEHICLE_CONTROL_KIDNAPPED 4 //Can't leave vehicle voluntarily, has to resist. +///controls the vehicles movement +#define VEHICLE_CONTROL_DRIVE (1<<0) +///Can't leave vehicle voluntarily, has to resist. +#define VEHICLE_CONTROL_KIDNAPPED (1<<1) +///melee attacks/shoves a vehicle may have +#define VEHICLE_CONTROL_MELEE (1<<2) +///using equipment/weapons on the vehicle +#define VEHICLE_CONTROL_EQUIPMENT (1<<3) +///changing around settings and the like. +#define VEHICLE_CONTROL_SETTINGS (1<<4) +//car_traits flags +///Will this car kidnap people by ramming into them? +#define CAN_KIDNAP (1<<0) -//Car trait flags -#define CAN_KIDNAP 1 +#define CLOWN_CANNON_INACTIVE 0 +#define CLOWN_CANNON_BUSY 1 +#define CLOWN_CANNON_READY 2 diff --git a/code/datums/achievements/_achievement_data.dm b/code/datums/achievements/_achievement_data.dm index 80f544a965..12c8256c74 100644 --- a/code/datums/achievements/_achievement_data.dm +++ b/code/datums/achievements/_achievement_data.dm @@ -109,7 +109,7 @@ /datum/achievement_data/ui_data(mob/user) var/ret_data = list() // screw standards (qustinnus you must rename src.data ok) - ret_data["categories"] = list("Bosses", "Misc", "Mafia", "Scores") + ret_data["categories"] = list("Bosses", "Jobs", "Misc", "Mafia", "Scores") ret_data["achievements"] = list() ret_data["user_key"] = user.ckey diff --git a/code/datums/achievements/_awards.dm b/code/datums/achievements/_awards.dm index 77485de8ba..22296b90f5 100644 --- a/code/datums/achievements/_awards.dm +++ b/code/datums/achievements/_awards.dm @@ -2,7 +2,7 @@ ///Name of the achievement, If null it won't show up in the achievement browser. (Handy for inheritance trees) var/name var/desc = "You did it." - ///Found in UI_Icons/Achievements + ///Found in ui_icons/achievements var/icon = "default" var/category = "Normal" @@ -78,7 +78,7 @@ /datum/award/achievement/on_unlock(mob/user) . = ..() - to_chat(user, "Achievement unlocked: [name]!") + to_chat(user, span_greenannounce("Achievement unlocked: [name]!")) ///Scores are for leaderboarded things, such as killcount of a specific boss /datum/award/score diff --git a/code/datums/achievements/boss_achievements.dm b/code/datums/achievements/boss_achievements.dm index 104a369405..f2ff90945f 100644 --- a/code/datums/achievements/boss_achievements.dm +++ b/code/datums/achievements/boss_achievements.dm @@ -12,7 +12,7 @@ name = "Boss Killer" desc = "You've come a long ways from asking how to switch hands." database_id = "Boss Killer" - // icon = "firstboss" + icon = "firstboss" /datum/award/achievement/boss/blood_miner_kill name = "Blood-Drunk Miner Killer" diff --git a/code/datums/achievements/job_achievements.dm b/code/datums/achievements/job_achievements.dm new file mode 100644 index 0000000000..13a68462ed --- /dev/null +++ b/code/datums/achievements/job_achievements.dm @@ -0,0 +1,34 @@ + +/datum/award/achievement/jobs + category = "Jobs" + icon = "basemisc" + +//chemistry + +/datum/award/achievement/jobs/chemistry_tut + name = "Perfect chemistry blossom" + desc = "Passed the chemistry tutorial with perfect purity!" + database_id = MEDAL_CHEM_TUT + icon = "chem_tut" + +//all of service! hip hip! + +/datum/award/achievement/jobs/service_bad + name = "Centcom Grade: Shitty Service" + desc = "Well, you at least tried. How about trying harder?" + database_id = MEDAL_BAD_SERVICE + icon = "chem_tut" + +/datum/award/achievement/jobs/service_okay + name = "Centcom Grade: Acceptable Service" + desc = "Well, it'll do! You and your department did just fine." + database_id = MEDAL_OKAY_SERVICE + icon = "chem_tut" + +/datum/award/achievement/jobs/service_good + name = "Centcom Grade: Exemplary Service" + desc = "Centcom is very impressed with your department!" + database_id = MEDAL_GOOD_SERVICE + icon = "chem_tut" + +//civilian achievies! while not recognized by the code, it is recognized by our hearts diff --git a/code/datums/achievements/job_scores.dm b/code/datums/achievements/job_scores.dm new file mode 100644 index 0000000000..d860c7e21a --- /dev/null +++ b/code/datums/achievements/job_scores.dm @@ -0,0 +1,14 @@ + +//chef + +/datum/award/score/chef_tourist_score + name = "Tourists Served as Chef Highscore" + desc = "Your highscore on serving tourist bots as chef." + database_id = CHEF_TOURISTS_SERVED + +//bartender + +/datum/award/score/bartender_tourist_score + name = "Tourists Served as Bartender Highscore" + desc = "Your highscore on serving tourist bots as bartender." + database_id = BARTENDER_TOURISTS_SERVED diff --git a/code/datums/achievements/misc_achievements.dm b/code/datums/achievements/misc_achievements.dm index 0da38df8f3..5ad337445b 100644 --- a/code/datums/achievements/misc_achievements.dm +++ b/code/datums/achievements/misc_achievements.dm @@ -154,8 +154,20 @@ database_id = MEDAL_TOOLBOX_SOUL icon = "toolbox_soul" -/datum/award/achievement/misc/chemistry_tut - name = "Perfect chemistry blossom" - desc = "Passed the chemistry tutorial with perfect purity!" - database_id = MEDAL_CHEM_TUT - icon = "chem_tut" +/datum/award/achievement/misc/hot_damn + name = "Hot Damn!" + desc = "Sometimes you need to make some noise to make a point." + database_id = MEDAL_HOT_DAMN + icon = "hotdamn" + +/datum/award/achievement/misc/cayenne_disk + name = "Very Important Piscis" + desc = "You can rest well now." + database_id = MEDAL_CAYENNE_DISK + icon = "cayenne_disk" + +/datum/award/achievement/misc/tram_surfer + name = "Tram Surfer" + desc = "Lights out, guerilla radio!" + database_id = MEDAL_TRAM_SURFER + icon = "tram_surfer" diff --git a/code/datums/achievements/misc_scores.dm b/code/datums/achievements/misc_scores.dm index 7ffc50c015..067cd68d7d 100644 --- a/code/datums/achievements/misc_scores.dm +++ b/code/datums/achievements/misc_scores.dm @@ -9,3 +9,9 @@ name = "Maintenance Pills Consumed" desc = "Wait why?" database_id = MAINTENANCE_PILL_SCORE + +///How high of a score on the Intento did we get? +/datum/award/score/intento_score + name = "Intento Score" + desc = "A blast from the future?" + database_id = INTENTO_SCORE diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm index 59464d25a7..683232b540 100644 --- a/code/game/objects/items/plushes.dm +++ b/code/game/objects/items/plushes.dm @@ -238,72 +238,73 @@ return return ..() -/obj/item/toy/plush/proc/love(obj/item/toy/plush/Kisser, mob/living/user) //~<3 - var/chance = 100 //to steal a kiss, surely there's a 100% chance no-one would reject a plush such as I? - var/concern = 20 //perhaps something might cloud true love with doubt - var/loyalty = 30 //why should another get between us? - var/duty = 50 //conquering another's is what I live for +/obj/item/toy/plush/proc/love(obj/item/toy/plush/Kisser, mob/living/user) //~<3 + var/chance = 100 //to steal a kiss, surely there's a 100% chance no-one would reject a plush such as I? + var/concern = 20 //perhaps something might cloud true love with doubt + var/loyalty = 30 //why should another get between us? + var/duty = 50 //conquering another's is what I live for //we are not catholic if(young == TRUE || Kisser.young == TRUE) - user.show_message("[src] plays tag with [Kisser].", MSG_VISUAL, - "They're happy.", 0) + user.show_message(span_notice("[src] plays tag with [Kisser]."), MSG_VISUAL, + span_notice("They're happy."), NONE) Kisser.cheer_up() cheer_up() //never again else if(Kisser in scorned) //message, visible, alternate message, neither visible nor audible - user.show_message("[src] rejects the advances of [Kisser]!", MSG_VISUAL, - "That didn't feel like it worked.", 0) + user.show_message(span_notice("[src] rejects the advances of [Kisser]!"), MSG_VISUAL, + span_notice("That didn't feel like it worked."), NONE) else if(src in Kisser.scorned) - user.show_message("[Kisser] realises who [src] is and turns away.", MSG_VISUAL, - "That didn't feel like it worked.", 0) + user.show_message(span_notice("[Kisser] realises who [src] is and turns away."), MSG_VISUAL, + span_notice("That didn't feel like it worked."), NONE) //first comes love - else if(Kisser.lover != src && Kisser.partner != src) //cannot be lovers or married - if(Kisser.lover) //if the initiator has a lover - Kisser.lover.heartbreak(Kisser) //the old lover can get over the kiss-and-run whilst the kisser has some fun - chance -= concern //one heart already broken, what does another mean? - if(lover) //if the recipient has a lover - chance -= loyalty //mustn't... but those lips - if(partner) //if the recipient has a partner - chance -= duty //do we mate for life? + else if(Kisser.lover != src && Kisser.partner != src) //cannot be lovers or married + if(Kisser.lover) //if the initiator has a lover + Kisser.lover.heartbreak(Kisser) //the old lover can get over the kiss-and-run whilst the kisser has some fun + chance -= concern //one heart already broken, what does another mean? + if(lover) //if the recipient has a lover + chance -= loyalty //mustn't... but those lips + if(partner) //if the recipient has a partner + chance -= duty //do we mate for life? - if(prob(chance)) //did we bag a date? - user.visible_message("[user] makes [Kisser] kiss [src]!", - "You make [Kisser] kiss [src]!") - if(lover) //who cares for the past, we live in the present + if(prob(chance)) //did we bag a date? + user.visible_message(span_notice("[user] makes [Kisser] kiss [src]!"), + span_notice("You make [Kisser] kiss [src]!")) + if(lover) //who cares for the past, we live in the present lover.heartbreak(src) new_lover(Kisser) Kisser.new_lover(src) else - user.show_message("[src] rejects the advances of [Kisser], maybe next time?", MSG_VISUAL, - "That didn't feel like it worked, this time.", 0) + user.show_message(span_notice("[src] rejects the advances of [Kisser], maybe next time?"), MSG_VISUAL, + span_notice("That didn't feel like it worked, this time."), NONE) //then comes marriage - else if(Kisser.lover == src && Kisser.partner != src) //need to be lovers (assumes loving is a two way street) but not married (also assumes similar) - user.visible_message("[user] pronounces [Kisser] and [src] married! D'aw.", - "You pronounce [Kisser] and [src] married!") + else if(Kisser.lover == src && Kisser.partner != src) //need to be lovers (assumes loving is a two way street) but not married (also assumes similar) + user.visible_message(span_notice("[user] pronounces [Kisser] and [src] married! D'aw."), + span_notice("You pronounce [Kisser] and [src] married!")) new_partner(Kisser) Kisser.new_partner(src) //then comes a baby in a baby's carriage, or an adoption in an adoption's orphanage - else if(Kisser.partner == src && !plush_child) //the one advancing does not take ownership of the child and we have a one child policy in the toyshop - user.visible_message("[user] is going to break [Kisser] and [src] by bashing them like that.", - "[Kisser] passionately embraces [src] in your hands. Look away you perv!") + else if(Kisser.partner == src && !plush_child) //the one advancing does not take ownership of the child and we have a one child policy in the toyshop + user.visible_message(span_notice("[user] is going to break [Kisser] and [src] by bashing them like that."), + span_notice("[Kisser] passionately embraces [src] in your hands. Look away you perv!")) + user.client.give_award(/datum/award/achievement/misc/rule8, user) if(plop(Kisser)) - user.visible_message("Something drops at the feet of [user].", - "The miracle of oh god did that just come out of [src]?!") + user.visible_message(span_notice("Something drops at the feet of [user]."), + span_notice("The miracle of oh god did that just come out of [src]?!")) //then comes protection, or abstinence if we are catholic else if(Kisser.partner == src && plush_child) - user.visible_message("[user] makes [Kisser] nuzzle [src]!", - "You make [Kisser] nuzzle [src]!") + user.visible_message(span_notice("[user] makes [Kisser] nuzzle [src]!"), + span_notice("You make [Kisser] nuzzle [src]!")) //then oh fuck something unexpected happened else - user.show_message("[Kisser] and [src] don't know what to do with one another.", 0) + user.show_message(span_warning("[Kisser] and [src] don't know what to do with one another."), NONE) /obj/item/toy/plush/proc/heartbreak(obj/item/toy/plush/Brutus) if(lover != Brutus) diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm index 7a067257cb..56cf50993b 100644 --- a/code/game/objects/items/storage/toolbox.dm +++ b/code/game/objects/items/storage/toolbox.dm @@ -79,6 +79,8 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons) name = "mechanical toolbox" icon_state = "blue" item_state = "toolbox_blue" + /// If FALSE, someone with a ensouled soulstone can sacrifice a spirit to change the sprite of this toolbox. + var/has_soul = FALSE /obj/item/storage/toolbox/mechanical/PopulateContents() new /obj/item/screwdriver(src) @@ -93,6 +95,7 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons) icon_state = "toolbox_blue_old" has_latches = FALSE can_rubberify = FALSE + has_soul = TRUE /obj/item/storage/toolbox/mechanical/old/heirloom name = "old, robust toolbox" //this will be named "X family toolbox" @@ -126,7 +129,7 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons) /obj/item/storage/toolbox/mechanical/old/clean/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) calc_damage() ..() - + /obj/item/storage/toolbox/mechanical/old/clean/PopulateContents() new /obj/item/screwdriver(src) new /obj/item/wrench(src) diff --git a/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm index 023be4b3e7..15ac8a6cb6 100644 --- a/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm +++ b/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm @@ -226,16 +226,16 @@ user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shed_human_form) if(!ishuman(user)) return - var/mob/living/carbon/human/H = user - H.physiology.brute_mod *= 0.5 - H.physiology.burn_mod *= 0.5 - var/datum/antagonist/heretic/heretic = user.mind.has_antag_datum(/datum/antagonist/heretic) - var/datum/eldritch_knowledge/flesh_grasp/ghoul1 = heretic.get_knowledge(/datum/eldritch_knowledge/flesh_grasp) - ghoul1.ghoul_amt *= 3 - var/datum/eldritch_knowledge/flesh_ghoul/ghoul2 = heretic.get_knowledge(/datum/eldritch_knowledge/flesh_ghoul) - ghoul2.max_amt *= 3 + var/mob/living/carbon/human/lord_of_arms = user + lord_of_arms.physiology.brute_mod *= 0.5 + lord_of_arms.physiology.burn_mod *= 0.5 + lord_of_arms.client?.give_award(/datum/award/achievement/misc/flesh_ascension, lord_of_arms) + var/datum/antagonist/heretic/heretic_datum = user.mind.has_antag_datum(/datum/antagonist/heretic) + var/datum/eldritch_knowledge/flesh_grasp/grasp_ghoul = heretic_datum.get_knowledge(/datum/eldritch_knowledge/flesh_grasp) + grasp_ghoul.ghoul_amt *= 3 + var/datum/eldritch_knowledge/flesh_ghoul/better_ghoul = heretic_datum.get_knowledge(/datum/eldritch_knowledge/flesh_ghoul) + better_ghoul.max_amt *= 3 - return ..() /datum/eldritch_knowledge/flesh_blade_upgrade_2 name = "Remembrance" diff --git a/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm index 509c35261e..733b2725cb 100644 --- a/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm +++ b/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm @@ -181,13 +181,13 @@ var/mob/living/carbon/human/H = user H.physiology.brute_mod *= 0.5 H.physiology.burn_mod *= 0.5 + H.client?.give_award(/datum/award/achievement/misc/rust_ascension, H) priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the decay, for the Rustbringer, [user.real_name] has ascended! None shall escape the corrosion! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg') new /datum/rust_spread(loc) var/datum/antagonist/heretic/ascension = H.mind.has_antag_datum(/datum/antagonist/heretic) ascension.ascended = TRUE return ..() - /datum/eldritch_knowledge/final/rust_final/on_life(mob/user) . = ..() if(!finished) diff --git a/code/modules/antagonists/eldritch_cult/knowledge/void_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/void_lore.dm index 97b5af0f4d..dcbc01b046 100644 --- a/code/modules/antagonists/eldritch_cult/knowledge/void_lore.dm +++ b/code/modules/antagonists/eldritch_cult/knowledge/void_lore.dm @@ -183,14 +183,14 @@ var/datum/weather/void_storm/storm /datum/eldritch_knowledge/final/void_final/on_finished_recipe(mob/living/user, list/atoms, loc) - var/mob/living/carbon/human/H = user - user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/repulse/eldritch) - H.physiology.brute_mod *= 0.5 - H.physiology.burn_mod *= 0.5 - ADD_TRAIT(H, TRAIT_RESISTLOWPRESSURE, MAGIC_TRAIT) - priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# The nobleman of void [H.real_name] has arrived, step along the Waltz that ends worlds! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg') - - sound_loop = new(list(user),TRUE,TRUE) + var/mob/living/carbon/human/waltzing = user + waltzing.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/repulse/eldritch) + waltzing.physiology.brute_mod *= 0.5 + waltzing.physiology.burn_mod *= 0.5 + ADD_TRAIT(waltzing, TRAIT_RESISTLOWPRESSURE, MAGIC_TRAIT) + waltzing.client?.give_award(/datum/award/achievement/misc/void_ascension, waltzing) + priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# The nobleman of void [waltzing.real_name] has arrived, step along the Waltz that ends worlds! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg') + sound_loop = new(user, TRUE, TRUE) return ..() /datum/eldritch_knowledge/final/void_final/on_death() diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index d4d70cd053..fe9770e64a 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -50,6 +50,14 @@ A.death() return ..() +/obj/item/soulstone/proc/hot_potato(mob/living/user) + to_chat(user, span_userdanger("Holy magics residing in \the [src] burn your hand!")) + var/obj/item/bodypart/affecting = user.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm") + affecting.receive_damage( 0, 10 ) // 10 burn damage + user.emote("scream") + user.update_damage_overlays() + user.dropItemToGround(src) + //////////////////////////////Capturing//////////////////////////////////////////////////////// /obj/item/soulstone/attack(mob/living/carbon/human/M, mob/living/user) @@ -94,6 +102,35 @@ to_chat(A, "You have been released from your prison, but you are still bound to the cult's will. Help them succeed in their goals at all costs.") was_used() +/obj/item/soulstone/pre_attack(atom/A, mob/living/user, params) + var/mob/living/simple_animal/hostile/construct/shade/occupant = (locate() in src) + var/obj/item/storage/toolbox/mechanical/target_toolbox = A + if(!occupant || !istype(target_toolbox) || target_toolbox.has_soul) + return ..() + + if(iscultist(user)) + hot_potato(user) + return + if(!iscultist(user, TRUE) && !iswizard(user) && !usability) + user.Unconscious(10 SECONDS) + to_chat(user, span_userdanger("Your body is wracked with debilitating pain!")) + return + + user.visible_message("[user] holds [src] above [user.p_their()] head and forces it into [target_toolbox] with a flash of light!", \ + span_notice("You hold [src] above your head briefly, then force it into [target_toolbox], transferring the [occupant]'s soul!"), ignored_mobs = occupant) + to_chat(occupant, span_userdanger("[user] holds you up briefly, then forces you into [target_toolbox]!")) + to_chat(occupant, span_deadsay("Your eternal soul has been sacrificed to restore the soul of a toolbox. Them's the breaks!")) + + occupant.client?.give_award(/datum/award/achievement/misc/toolbox_soul, occupant) + occupant.deathmessage = "shrieks out in unholy pain as [occupant.p_their()] soul is absorbed into [target_toolbox]!" + release_shades(user, TRUE) + occupant.death() + + target_toolbox.name = "soulful toolbox" + target_toolbox.icon_state = "toolbox_blue_old" + target_toolbox.has_soul = TRUE + target_toolbox.has_latches = FALSE + ///////////////////////////Transferring to constructs///////////////////////////////////////////////////// /obj/structure/constructshell name = "empty shell" diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm index 8388d6501a..747abb4b19 100644 --- a/code/modules/mob/living/simple_animal/hostile/carp.dm +++ b/code/modules/mob/living/simple_animal/hostile/carp.dm @@ -95,20 +95,78 @@ /mob/living/simple_animal/hostile/carp/cayenne name = "Cayenne" + real_name = "Cayenne" desc = "A failed Syndicate experiment in weaponized space carp technology, it now serves as a lovable mascot." gender = FEMALE - regen_amount = 8 - speak_emote = list("squeaks") - maxHealth = 90 - health = 90 - gold_core_spawnable = NO_SPAWN - faction = list(ROLE_SYNDICATE, "carp") //They are still a carp AIStatus = AI_OFF + gold_core_spawnable = NO_SPAWN + faction = list(ROLE_SYNDICATE) + /// Keeping track of the nuke disk for the functionality of storing it. + var/obj/item/disk/nuclear/disky + /// Location of the file storing disk overlays + // var/icon/disk_overlay_file = 'icons/mob/carp.dmi' + /// Colored disk mouth appearance for adding it as a mouth overlay + var/mutable_appearance/colored_disk_mouth - harm_intent_damage = 12 - obj_damage = 70 - melee_damage_lower = 15 - melee_damage_upper = 18 +/mob/living/simple_animal/hostile/carp/cayenne/Initialize() + . = ..() + // AddElement(/datum/element/pet_bonus, "bloops happily!") + // colored_disk_mouth = mutable_appearance(SSgreyscale.GetColoredIconByType(/datum/greyscale_config/carp/disk_mouth, greyscale_colors), "disk_mouth") + ADD_TRAIT(src, TRAIT_DISK_VERIFIER, INNATE_TRAIT) //carp can verify disky + +/mob/living/simple_animal/hostile/carp/cayenne/IsAdvancedToolUser() + return TRUE //carp SMART + +/mob/living/simple_animal/hostile/carp/cayenne/death(gibbed) + if(disky) + disky.forceMove(drop_location()) + disky = null + return ..() + +/mob/living/simple_animal/hostile/carp/cayenne/Destroy(force) + QDEL_NULL(disky) + return ..() + +/mob/living/simple_animal/hostile/carp/cayenne/examine(mob/user) + . = ..() + if(disky) + . += span_notice("Wait... is that [disky] in [p_their()] mouth?") + +/mob/living/simple_animal/hostile/carp/cayenne/AttackingTarget(atom/attacked_target) + if(istype(attacked_target, /obj/item/disk/nuclear)) + var/obj/item/disk/nuclear/potential_disky = attacked_target + if(potential_disky.anchored) + return + potential_disky.forceMove(src) + disky = potential_disky + to_chat(src, span_nicegreen("YES!! You manage to pick up [disky]. (Click anywhere to place it back down.)")) + update_icon() + if(!disky.fake) + client.give_award(/datum/award/achievement/misc/cayenne_disk, src) + return + if(disky) + if(isopenturf(attacked_target)) + to_chat(src, span_notice("You place [disky] on [attacked_target]")) + disky.forceMove(attacked_target.drop_location()) + disky = null + update_icon() + else + disky.melee_attack_chain(src, attacked_target) + return + return ..() + +/mob/living/simple_animal/hostile/carp/cayenne/Exited(atom/movable/gone, direction) + . = ..() + if(disky == gone) + disky = null + update_icon() + +/mob/living/simple_animal/hostile/carp/cayenne/update_overlays() + . = ..() + if(!disky || stat == DEAD) + return + // . += colored_disk_mouth + // . += mutable_appearance(disk_overlay_file, "disk_overlay") #undef REGENERATION_DELAY diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm index 869f29951b..938483be84 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm @@ -32,6 +32,9 @@ Difficulty: Hard wander = FALSE del_on_death = TRUE blood_volume = BLOOD_VOLUME_NORMAL + achievement_type = /datum/award/achievement/boss/wendigo_kill + crusher_achievement_type = /datum/award/achievement/boss/wendigo_crusher + score_achievement_type = /datum/award/score/wendigo_score deathmessage = "falls, shaking the ground around it" deathsound = 'sound/effects/gravhit.ogg' attack_action_types = list(/datum/action/innate/megafauna_attack/heavy_stomp, diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/_vehicle.dm index 12e9f365d0..d0144269b7 100644 --- a/code/modules/vehicles/_vehicle.dm +++ b/code/modules/vehicles/_vehicle.dm @@ -91,9 +91,9 @@ /obj/vehicle/proc/after_add_occupant(mob/M) auto_assign_occupant_flags(M) -/obj/vehicle/proc/auto_assign_occupant_flags(mob/M) //override for each type that needs it. Default is assign driver if drivers is not at max. +/obj/vehicle/proc/auto_assign_occupant_flags(mob/M) //override for each type that needs it. Default is assign driver if drivers is not at max. if(driver_amount() < max_drivers) - add_control_flags(M, VEHICLE_CONTROL_DRIVE|VEHICLE_CONTROL_PERMISSION) + add_control_flags(M, VEHICLE_CONTROL_DRIVE) /obj/vehicle/proc/remove_occupant(mob/M) if(!istype(M)) diff --git a/code/modules/vehicles/cars/car.dm b/code/modules/vehicles/cars/car.dm index 08a7986fa3..0196abe82c 100644 --- a/code/modules/vehicles/cars/car.dm +++ b/code/modules/vehicles/cars/car.dm @@ -16,7 +16,7 @@ . = ..() initialize_controller_action_type(/datum/action/vehicle/sealed/remove_key, VEHICLE_CONTROL_DRIVE) if(car_traits & CAN_KIDNAP) - initialize_controller_action_type(/datum/action/vehicle/sealed/DumpKidnappedMobs, VEHICLE_CONTROL_DRIVE) + initialize_controller_action_type(/datum/action/vehicle/sealed/dump_kidnapped_mobs, VEHICLE_CONTROL_DRIVE) /obj/vehicle/sealed/car/driver_move(mob/user, direction) if(key_type && !is_key(inserted_key)) diff --git a/code/modules/vehicles/cars/clowncar.dm b/code/modules/vehicles/cars/clowncar.dm index 0b75dbf4db..c4da65a902 100644 --- a/code/modules/vehicles/cars/clowncar.dm +++ b/code/modules/vehicles/cars/clowncar.dm @@ -1,6 +1,6 @@ /obj/vehicle/sealed/car/clowncar name = "clown car" - desc = "How someone could even fit in there is beyond me." + desc = "How someone could even fit in there is byond me." icon_state = "clowncar" max_integrity = 150 armor = list("melee" = 70, "bullet" = 40, "laser" = 40, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) @@ -10,25 +10,36 @@ car_traits = CAN_KIDNAP key_type = /obj/item/bikehorn key_type_exact = FALSE - var/droppingoil = FALSE - var/RTDcooldown = 150 - var/lastRTDtime = 0 + ///list of headlight colors we use to pick through when we have party mode due to emag + var/headlight_colors = list(COLOR_RED, COLOR_ORANGE, COLOR_YELLOW, COLOR_LIME, COLOR_BRIGHT_BLUE, COLOR_CYAN, COLOR_PURPLE) + ///Cooldown time inbetween [/obj/vehicle/sealed/car/clowncar/proc/roll_the_dice()] usages + var/dice_cooldown_time = 150 + ///How many times kidnappers in the clown car said thanks + var/thankscount = 0 + ///Current status of the cannon, alternates between CLOWN_CANNON_INACTIVE, CLOWN_CANNON_BUSY and CLOWN_CANNON_READY + var/cannonmode = CLOWN_CANNON_INACTIVE + var/light_on = TRUE + +/obj/vehicle/sealed/car/clowncar/Initialize() + . = ..() + START_PROCESSING(SSobj,src) + +/obj/vehicle/sealed/car/clowncar/process() + if(light_on && (obj_flags & EMAGGED)) + set_light_color(pick(headlight_colors)) /obj/vehicle/sealed/car/clowncar/generate_actions() . = ..() - initialize_controller_action_type(/datum/action/vehicle/sealed/horn/clowncar, VEHICLE_CONTROL_DRIVE) - -/obj/vehicle/sealed/car/clowncar/driver_move(mob/user, direction) //Prevent it from moving onto space - if(isspaceturf(get_step(src, direction))) - return FALSE - else - return ..() + initialize_controller_action_type(/datum/action/vehicle/sealed/horn, VEHICLE_CONTROL_DRIVE) + initialize_controller_action_type(/datum/action/vehicle/sealed/thank, VEHICLE_CONTROL_KIDNAPPED) /obj/vehicle/sealed/car/clowncar/auto_assign_occupant_flags(mob/M) if(ishuman(M)) var/mob/living/carbon/human/H = M if(H.mind && HAS_TRAIT(H.mind, TRAIT_CLOWN_MENTALITY)) //Ensures only clowns can drive the car. (Including more at once) - add_control_flags(M, VEHICLE_CONTROL_DRIVE|VEHICLE_CONTROL_PERMISSION) + add_control_flags(H, VEHICLE_CONTROL_DRIVE) + RegisterSignal(H, COMSIG_MOB_CLICKON, .proc/fire_cannon_at) + M.log_message("has entered [src] as a possible driver", LOG_ATTACK) return add_control_flags(M, VEHICLE_CONTROL_KIDNAPPED) @@ -36,106 +47,195 @@ . = ..() playsound(src, pick('sound/vehicles/clowncar_load1.ogg', 'sound/vehicles/clowncar_load2.ogg'), 75) +/obj/vehicle/sealed/car/clowncar/after_add_occupant(mob/M, control_flags) + . = ..() + if(return_controllers_with_flag(VEHICLE_CONTROL_KIDNAPPED).len >= 30) + for(var/mob/voreman as anything in return_drivers()) + voreman.client.give_award(/datum/award/achievement/misc/round_and_full, voreman) + +/obj/vehicle/sealed/car/clowncar/attack_animal(mob/living/simple_animal/user, list/modifiers) + if((user.loc != src) || user.environment_smash & (ENVIRONMENT_SMASH_WALLS|ENVIRONMENT_SMASH_RWALLS)) + return ..() + +/obj/vehicle/sealed/car/clowncar/mob_exit(mob/M, silent = FALSE, randomstep = FALSE) + . = ..() + UnregisterSignal(M, COMSIG_MOB_CLICKON) + /obj/vehicle/sealed/car/clowncar/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) . = ..() if(prob(33)) - visible_message("[src] spews out a ton of space lube!") + visible_message(span_danger("[src] spews out a ton of space lube!")) new /obj/effect/particle_effect/foam(loc) //YEET -/obj/vehicle/sealed/car/clowncar/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1) +/obj/vehicle/sealed/car/clowncar/attacked_by(obj/item/I, mob/living/user) . = ..() - if(istype(I, /obj/item/reagent_containers/food/snacks/grown/banana)) - var/obj/item/reagent_containers/food/snacks/grown/banana/banana = I - obj_integrity += min(banana.seed.potency, max_integrity-obj_integrity) - to_chat(user, "You use the [banana] to repair the [src]!") - qdel(banana) + if(!istype(I, /obj/item/reagent_containers/food/snacks/grown/banana)) + return + var/obj/item/reagent_containers/food/snacks/grown/banana/banana = I + obj_integrity += min(banana.seed.potency, max_integrity-obj_integrity) + to_chat(user, span_danger("You use the [banana] to repair the [src]!")) + qdel(banana) -/obj/vehicle/sealed/car/clowncar/Bump(atom/movable/M) +/obj/vehicle/sealed/car/clowncar/Bump(atom/bumped) . = ..() - if(isliving(M)) - if(ismegafauna(M)) + if(isliving(bumped)) + if(ismegafauna(bumped)) return - var/mob/living/L = M - if(iscarbon(L)) - var/mob/living/carbon/C = L - C.DefaultCombatKnockdown(40) //I play to make sprites go horizontal - L.visible_message("[src] rams into [L] and sucks him up!") //fuck off shezza this isn't ERP. - mob_forced_enter(L) + var/mob/living/hittarget_living = bumped + if(iscarbon(hittarget_living)) + var/mob/living/carbon/carb = hittarget_living + carb.DefaultCombatKnockdown(40) //I play to make sprites go horizontal + hittarget_living.visible_message(span_warning("[src] rams into [hittarget_living] and sucks [hittarget_living.p_them()] up!")) //fuck off shezza this isn't ERP. + mob_forced_enter(hittarget_living) playsound(src, pick('sound/vehicles/clowncar_ram1.ogg', 'sound/vehicles/clowncar_ram2.ogg', 'sound/vehicles/clowncar_ram3.ogg'), 75) - else if(istype(M, /turf/closed) || istype(M, /obj/machinery/door/airlock/external)) - visible_message("[src] rams into [M] and crashes!") - playsound(src, pick('sound/vehicles/clowncar_crash1.ogg', 'sound/vehicles/clowncar_crash2.ogg'), 75) - playsound(src, 'sound/vehicles/clowncar_crashpins.ogg', 75) - DumpMobs(TRUE) + log_combat(src, hittarget_living, "sucked up") + return + if(!istype(bumped, /turf/closed)) + return + visible_message(span_warning("[src] rams into [bumped] and crashes!")) + playsound(src, pick('sound/vehicles/clowncar_crash1.ogg', 'sound/vehicles/clowncar_crash2.ogg'), 75) + playsound(src, 'sound/vehicles/clowncar_crashpins.ogg', 75) + DumpMobs(TRUE) + log_combat(src, bumped, "crashed into", null, "dumping all passengers") /obj/vehicle/sealed/car/clowncar/emag_act(mob/user) . = ..() if(obj_flags & EMAGGED) return obj_flags |= EMAGGED - to_chat(user, "You scramble the clowncar child safety lock and a panel with 6 colorful buttons appears!") - initialize_controller_action_type(/datum/action/vehicle/sealed/RollTheDice, VEHICLE_CONTROL_DRIVE) + to_chat(user, span_danger("You scramble \the [src]'s child safety lock, and a panel with six colorful buttons appears!")) + initialize_controller_action_type(/datum/action/vehicle/sealed/roll_the_dice, VEHICLE_CONTROL_DRIVE) + initialize_controller_action_type(/datum/action/vehicle/sealed/cannon, VEHICLE_CONTROL_DRIVE) return TRUE -/obj/vehicle/sealed/car/clowncar/Destroy() +/obj/vehicle/sealed/car/clowncar/obj_destruction(damage_flag) playsound(src, 'sound/vehicles/clowncar_fart.ogg', 100) + STOP_PROCESSING(SSobj,src) return ..() -/obj/vehicle/sealed/car/clowncar/after_move(direction) - . = ..() - if(droppingoil) - new /obj/effect/decal/cleanable/oil/slippery(loc) - -/obj/vehicle/sealed/car/clowncar/proc/RollTheDice(mob/user) - if(world.time - lastRTDtime < RTDcooldown) - to_chat(user, "The button panel is currently recharging.") +/** + * Plays a random funky effect + * Only available while car is emagged + * Possible effects: + * * Spawn bananapeel + * * Spawn random reagent foam + * * Make the clown car look like a singulo temporarily + * * Spawn Laughing chem gas + * * Drop oil + * * Fart and make everyone nearby laugh + */ +/obj/vehicle/sealed/car/clowncar/proc/roll_the_dice(mob/user) + if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_CLOWNCAR_RANDOMNESS)) + to_chat(user, span_notice("The button panel is currently recharging.")) return - lastRTDtime = world.time - var/randomnum = rand(1,6) - switch(randomnum) + TIMER_COOLDOWN_START(src, COOLDOWN_CLOWNCAR_RANDOMNESS, dice_cooldown_time) + switch(rand(1,6)) if(1) - visible_message("[user] has pressed one of the colorful buttons on [src] and a special banana peel drops out of it.") + visible_message(span_danger("[user] presses one of the colorful buttons on [src], and a special banana peel drops out of it.")) new /obj/item/grown/bananapeel/specialpeel(loc) if(2) - visible_message("[user] has pressed one of the colorful buttons on [src] and unknown chemicals flood out of it.") - var/datum/reagents/R = new/datum/reagents(300) - R.my_atom = src - R.add_reagent(get_random_reagent_id(), 100) + visible_message(span_danger("[user] presses one of the colorful buttons on [src], and unknown chemicals flood out of it.")) + var/datum/reagents/randomchems = new/datum/reagents(300) + randomchems.my_atom = src + randomchems.add_reagent(get_random_reagent_id(), 100) var/datum/effect_system/foam_spread/foam = new - foam.set_up(200, loc, R) + foam.set_up(200, loc, randomchems) foam.start() if(3) - visible_message("[user] has pressed one of the colorful buttons on [src] and the clown car turns on its singularity disguise system.") + visible_message(span_danger("[user] presses one of the colorful buttons on [src], and the clown car turns on its singularity disguise system.")) icon = 'icons/obj/singularity.dmi' icon_state = "singularity_s1" - addtimer(CALLBACK(src, .proc/ResetIcon), 100) + addtimer(CALLBACK(src, .proc/reset_icon), 10 SECONDS) if(4) - visible_message("[user] has pressed one of the colorful buttons on [src] and the clown car spews out a cloud of laughing gas.") - var/datum/reagents/R = new/datum/reagents(300) - R.my_atom = src - R.add_reagent(/datum/reagent/consumable/superlaughter, 50) + visible_message(span_danger("[user] presses one of the colorful buttons on [src], and the clown car spews out a cloud of laughing gas.")) + var/datum/reagents/funnychems = new/datum/reagents(300) + funnychems.my_atom = src + funnychems.add_reagent(/datum/reagent/consumable/superlaughter, 50) var/datum/effect_system/smoke_spread/chem/smoke = new() - smoke.set_up(R, 4) + smoke.set_up(funnychems, 4) smoke.attach(src) smoke.start() if(5) - visible_message("[user] has pressed one of the colorful buttons on [src] and the clown car starts dropping an oil trail.") - droppingoil = TRUE - addtimer(CALLBACK(src, .proc/StopDroppingOil), 30) + visible_message(span_danger("[user] presses one of the colorful buttons on [src], and the clown car starts dropping an oil trail.")) + RegisterSignal(src, COMSIG_MOVABLE_MOVED, .proc/cover_in_oil) + addtimer(CALLBACK(src, .proc/stop_dropping_oil), 3 SECONDS) if(6) - visible_message("[user] has pressed one of the colorful buttons on [src] and the clown car lets out a comedic toot.") + visible_message(span_danger("[user] presses one of the colorful buttons on [src], and the clown car lets out a comedic toot.")) playsound(src, 'sound/vehicles/clowncar_fart.ogg', 100) for(var/mob/living/L in orange(loc, 6)) L.emote("laughs") - for(var/mob/living/L in occupants) + for(var/mob/living/L as anything in occupants) L.emote("laughs") -/obj/vehicle/sealed/car/clowncar/proc/ResetIcon() +///resets the icon and iconstate of the clowncar after it was set to singulo states +/obj/vehicle/sealed/car/clowncar/proc/reset_icon() icon = initial(icon) icon_state = initial(icon_state) -/obj/vehicle/sealed/car/clowncar/proc/StopDroppingOil() - droppingoil = FALSE +///Deploys oil when the clowncar moves in oil deploy mode +/obj/vehicle/sealed/car/clowncar/proc/cover_in_oil() + SIGNAL_HANDLER + new /obj/effect/decal/cleanable/oil/slippery(loc) + +///Stops dropping oil after the time has run up +/obj/vehicle/sealed/car/clowncar/proc/stop_dropping_oil() + UnregisterSignal(src, COMSIG_MOVABLE_MOVED) + +///Toggles the on and off state of the clown cannon that shoots random kidnapped people +/obj/vehicle/sealed/car/clowncar/proc/toggle_cannon(mob/user) + if(cannonmode == CLOWN_CANNON_BUSY) + to_chat(user, span_notice("Please wait for the vehicle to finish its current action first.")) + return + if(cannonmode) //canon active, deactivate + flick("clowncar_fromfire", src) + icon_state = "clowncar" + addtimer(CALLBACK(src, .proc/deactivate_cannon), 2 SECONDS) + playsound(src, 'sound/vehicles/clowncar_cannonmode2.ogg', 75) + visible_message(span_danger("The [src] starts going back into mobile mode.")) + else + canmove = FALSE //anchor and activate canon + flick("clowncar_tofire", src) + icon_state = "clowncar_fire" + visible_message(span_danger("The [src] opens up and reveals a large cannon.")) + addtimer(CALLBACK(src, .proc/activate_cannon), 2 SECONDS) + playsound(src, 'sound/vehicles/clowncar_cannonmode1.ogg', 75) + cannonmode = CLOWN_CANNON_BUSY + +///Finalizes canon activation +/obj/vehicle/sealed/car/clowncar/proc/activate_cannon() + // mouse_pointer = 'icons/effects/mouse_pointers/mecha_mouse.dmi' + cannonmode = CLOWN_CANNON_READY + for(var/mob/living/driver as anything in return_controllers_with_flag(VEHICLE_CONTROL_DRIVE)) + driver.update_mouse_pointer() + +///Finalizes canon deactivation +/obj/vehicle/sealed/car/clowncar/proc/deactivate_cannon() + canmove = TRUE + // mouse_pointer = null + cannonmode = CLOWN_CANNON_INACTIVE + for(var/mob/living/driver as anything in return_controllers_with_flag(VEHICLE_CONTROL_DRIVE)) + driver.update_mouse_pointer() + +///Fires the cannon where the user clicks +/obj/vehicle/sealed/car/clowncar/proc/fire_cannon_at(mob/user, atom/A, params) + SIGNAL_HANDLER + if(cannonmode != CLOWN_CANNON_READY || !length(return_controllers_with_flag(VEHICLE_CONTROL_KIDNAPPED))) + return NONE + var/mob/living/unlucky_sod = pick(return_controllers_with_flag(VEHICLE_CONTROL_KIDNAPPED)) + mob_exit(unlucky_sod, TRUE) + flick("clowncar_recoil", src) + playsound(src, pick('sound/vehicles/carcannon1.ogg', 'sound/vehicles/carcannon2.ogg', 'sound/vehicles/carcannon3.ogg'), 75) + unlucky_sod.throw_at(A, 10, 2) + log_combat(user, unlucky_sod, "fired", src, "towards [A]") //this doesn't catch if the mob hits something between the car and the target + return COMSIG_MOB_CANCEL_CLICKON + +///Increments the thanks counter every time someone thats been kidnapped thanks the driver +/obj/vehicle/sealed/car/clowncar/proc/increment_thanks_counter() + thankscount++ + if(thankscount < 100) + return + for(var/mob/busdriver as anything in return_drivers()) + busdriver.client.give_award(/datum/award/achievement/misc/the_best_driver, busdriver) /obj/vehicle/sealed/car/clowncar/twitch_plays key_type = null @@ -144,12 +244,10 @@ /obj/vehicle/sealed/car/clowncar/twitch_plays/Initialize() . = ..() AddComponent(/datum/component/twitch_plays/simple_movement) - START_PROCESSING(SSfastprocess, src) GLOB.poi_list |= src notify_ghosts("Twitch Plays: Clown Car") /obj/vehicle/sealed/car/clowncar/twitch_plays/Destroy() - STOP_PROCESSING(SSfastprocess, src) GLOB.poi_list -= src return ..() @@ -158,3 +256,4 @@ if(!dir) return driver_move(null, dir) + ..() diff --git a/code/modules/vehicles/vehicle_actions.dm b/code/modules/vehicles/vehicle_actions.dm index 5de2c8961f..8b2c72008c 100644 --- a/code/modules/vehicles/vehicle_actions.dm +++ b/code/modules/vehicles/vehicle_actions.dm @@ -126,44 +126,70 @@ desc = "Honk your classy horn." button_icon_state = "car_horn" var/hornsound = 'sound/items/carhorn.ogg' - var/last_honk_time /datum/action/vehicle/sealed/horn/Trigger() - if(world.time - last_honk_time > 20) - vehicle_entered_target.visible_message("[vehicle_entered_target] loudly honks") - to_chat(owner, "You press the vehicle's horn.") - playsound(vehicle_entered_target, hornsound, 75) - last_honk_time = world.time + if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_CAR_HONK)) + return + TIMER_COOLDOWN_START(src, COOLDOWN_CAR_HONK, 2 SECONDS) + vehicle_entered_target.visible_message(span_danger("[vehicle_entered_target] loudly honks!")) + to_chat(owner, span_notice("You press [vehicle_entered_target]'s horn.")) + if(istype(vehicle_target.inserted_key, /obj/item/bikehorn)) + vehicle_target.inserted_key.attack_self(owner) //The bikehorn plays a sound instead + return + playsound(vehicle_entered_target, hornsound, 75) -/datum/action/vehicle/sealed/horn/clowncar/Trigger() - if(world.time - last_honk_time > 20) - vehicle_entered_target.visible_message("[vehicle_entered_target] loudly honks") - to_chat(owner, "You press the vehicle's horn.") - last_honk_time = world.time - if(vehicle_target.inserted_key) - vehicle_target.inserted_key.attack_self(owner) //The key plays a sound - else - playsound(vehicle_entered_target, hornsound, 75) - -/datum/action/vehicle/sealed/DumpKidnappedMobs - name = "Dump kidnapped mobs" +/datum/action/vehicle/sealed/dump_kidnapped_mobs + name = "Dump Kidnapped Mobs" desc = "Dump all objects and people in your car on the floor." button_icon_state = "car_dump" -/datum/action/vehicle/sealed/DumpKidnappedMobs/Trigger() - vehicle_entered_target.visible_message("[vehicle_entered_target] starts dumping the people inside of it.") +/datum/action/vehicle/sealed/dump_kidnapped_mobs/Trigger() + vehicle_entered_target.visible_message(span_danger("[vehicle_entered_target] starts dumping the people inside of it.")) vehicle_entered_target.DumpSpecificMobs(VEHICLE_CONTROL_KIDNAPPED) -/datum/action/vehicle/sealed/RollTheDice - name = "Press a colorful button" +/datum/action/vehicle/sealed/roll_the_dice + name = "Press Colorful Button" desc = "Press one of those colorful buttons on your display panel!" button_icon_state = "car_rtd" -/datum/action/vehicle/sealed/RollTheDice/Trigger() - if(istype(vehicle_entered_target, /obj/vehicle/sealed/car/clowncar)) - var/obj/vehicle/sealed/car/clowncar/C = vehicle_entered_target - C.RollTheDice(owner) +/datum/action/vehicle/sealed/roll_the_dice/Trigger() + if(!istype(vehicle_entered_target, /obj/vehicle/sealed/car/clowncar)) + return + var/obj/vehicle/sealed/car/clowncar/C = vehicle_entered_target + C.roll_the_dice(owner) + +/datum/action/vehicle/sealed/cannon + name = "Toggle Siege Mode" + desc = "Destroy them with their own fodder!" + button_icon_state = "car_cannon" + +/datum/action/vehicle/sealed/cannon/Trigger() + if(!istype(vehicle_entered_target, /obj/vehicle/sealed/car/clowncar)) + return + var/obj/vehicle/sealed/car/clowncar/C = vehicle_entered_target + C.toggle_cannon(owner) + + +/datum/action/vehicle/sealed/thank + name = "Thank the Clown Car Driver" + desc = "They're just doing their job." + button_icon_state = "car_thanktheclown" + COOLDOWN_DECLARE(thank_time_cooldown) + + +/datum/action/vehicle/sealed/thank/Trigger() + if(!istype(vehicle_entered_target, /obj/vehicle/sealed/car/clowncar)) + return + if(!COOLDOWN_FINISHED(src, thank_time_cooldown)) + return + COOLDOWN_START(src, thank_time_cooldown, 6 SECONDS) + var/obj/vehicle/sealed/car/clowncar/clown_car = vehicle_entered_target + var/mob/living/carbon/human/clown = pick(clown_car.return_drivers()) + if(!clown) + return + owner.say("Thank you for the fun ride, [clown.name]!") + clown_car.increment_thanks_counter() /datum/action/vehicle/ridden/scooter/skateboard/ollie @@ -197,7 +223,9 @@ L.Move(landing_turf, vehicle_target.dir) passtable_off(L, VEHICLE_TRAIT) V.pass_flags &= ~PASSTABLE - if(locate(/obj/structure/table) in V.loc.contents) + if((locate(/obj/structure/table) in V.loc.contents) || (locate(/obj/structure/fluff/railing) in V.loc.contents)) + if(locate(/obj/structure/fluff/railing) in V.loc.contents) + L.client.give_award(/datum/award/achievement/misc/tram_surfer, L) V.grinding = TRUE V.icon_state = "[V.board_icon]-grind" addtimer(CALLBACK(V, /obj/vehicle/ridden/scooter/skateboard/.proc/grind), 2) diff --git a/sound/vehicles/carcannon1.ogg b/sound/vehicles/carcannon1.ogg new file mode 100644 index 0000000000..751fa6f754 Binary files /dev/null and b/sound/vehicles/carcannon1.ogg differ diff --git a/sound/vehicles/carcannon2.ogg b/sound/vehicles/carcannon2.ogg new file mode 100644 index 0000000000..7bc86d7cbc Binary files /dev/null and b/sound/vehicles/carcannon2.ogg differ diff --git a/sound/vehicles/carcannon3.ogg b/sound/vehicles/carcannon3.ogg new file mode 100644 index 0000000000..80407e553f Binary files /dev/null and b/sound/vehicles/carcannon3.ogg differ diff --git a/sound/vehicles/clowncar_cannonmode1.ogg b/sound/vehicles/clowncar_cannonmode1.ogg new file mode 100644 index 0000000000..aa21c8f990 Binary files /dev/null and b/sound/vehicles/clowncar_cannonmode1.ogg differ diff --git a/sound/vehicles/clowncar_cannonmode2.ogg b/sound/vehicles/clowncar_cannonmode2.ogg new file mode 100644 index 0000000000..931e146422 Binary files /dev/null and b/sound/vehicles/clowncar_cannonmode2.ogg differ diff --git a/tgstation.dme b/tgstation.dme index fea0217b78..86814261c5 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -458,6 +458,8 @@ #include "code\datums\achievements\_awards.dm" #include "code\datums\achievements\boss_achievements.dm" #include "code\datums\achievements\boss_scores.dm" +#include "code\datums\achievements\job_achievements.dm" +#include "code\datums\achievements\job_scores.dm" #include "code\datums\achievements\mafia_achievements.dm" #include "code\datums\achievements\misc_achievements.dm" #include "code\datums\achievements\misc_scores.dm"