From 4b7e693d0506df7a1339c3c9df0d20d82961466d Mon Sep 17 00:00:00 2001 From: hyperioo <64754494+hyperioo@users.noreply.github.com> Date: Fri, 20 Oct 2023 19:07:49 +0200 Subject: [PATCH] New mid-threat antag: Pirates (#8340) * Start working on pirates * Add pirate roleset * Continue working on pirates * Add plunder objective * Continue working on pirate stuff * Increase plunder timer * Plunder value scales with number of pirates * Finish cleaning and tuning * 10k to 15k * Properly update explanation * Don't get whole faction inventory * Increase shuttle move time * Add shield diffusers * Shuttle lock system * Slightly shift shuttle to avoid seeing the deges * Update pirate welcome * Hopefully fix pirate comms * Remove useless helmet since they're already in void suits * Oopsies * Remove pirate shuttle docking controller * Add hacked cigarette machine and rum keg * Remove debug message * Make pirate base item worthless * Forgot that for shuttle controller --- cev_eris.dme | 6 + code/__DEFINES/gamemode.dm | 2 + code/__DEFINES/items_clothing.dm | 1 + code/controllers/communications.dm | 4 +- code/datums/objective/pirate_plunder.dm | 111 + code/datums/objective/plunder.dm | 58 + code/datums/outfits/antagonist/pirate.dm | 29 + code/game/antagonist/antagonist_equip.dm | 2 + code/game/antagonist/antagonist_factions.dm | 1 + code/game/antagonist/outer/pirate/pirate.dm | 142 + .../antagonist/outer/pirate/pirate_faction.dm | 52 + code/game/area/Space Station 13 areas.dm | 11 + .../gamemodes/events/weather/rad_storm.dm | 2 +- code/game/gamemodes/roleset/faction/pirate.dm | 19 + code/game/jobs/access_datum.dm | 4 + code/game/machinery/telecomms/broadcaster.dm | 2 + .../items/devices/radio/encryptionkey.dm | 6 + .../objects/items/devices/radio/headset.dm | 12 + .../game/objects/items/devices/radio/radio.dm | 3 +- code/game/objects/landmarks/storyevent.dm | 5 + code/modules/cargo/exports/large_objects.dm | 4 + code/modules/mob/living/say.dm | 2 + code/modules/reagents/reagent_dispenser.dm | 11 + code/modules/research/message_server.dm | 2 + code/modules/shuttles/antagonist.dm | 5 + icons/mob/mob.dmi | Bin 195458 -> 195541 bytes maps/CEVEris/_CEV_Eris.dmm | 56 +- maps/CEVEris/centcomm.dmm | 2884 ++++++++++------- maps/CEVEris/shuttles-eris.dm | 121 + 29 files changed, 2437 insertions(+), 1120 deletions(-) create mode 100644 code/datums/objective/pirate_plunder.dm create mode 100644 code/datums/objective/plunder.dm create mode 100644 code/datums/outfits/antagonist/pirate.dm create mode 100644 code/game/antagonist/outer/pirate/pirate.dm create mode 100644 code/game/antagonist/outer/pirate/pirate_faction.dm create mode 100644 code/game/gamemodes/roleset/faction/pirate.dm diff --git a/cev_eris.dme b/cev_eris.dme index 445138e0ad9..80c881b63c6 100644 --- a/cev_eris.dme +++ b/cev_eris.dme @@ -393,6 +393,8 @@ #include "code\datums\objective\merc_mission.dm" #include "code\datums\objective\nuclear.dm" #include "code\datums\objective\objective.dm" +#include "code\datums\objective\pirate_plunder.dm" +#include "code\datums\objective\plunder.dm" #include "code\datums\objective\protect.dm" #include "code\datums\objective\rev.dm" #include "code\datums\objective\silence.dm" @@ -435,6 +437,7 @@ #include "code\datums\outfits\outfit.dm" #include "code\datums\outfits\antagonist\antagonist.dm" #include "code\datums\outfits\antagonist\mercenary.dm" +#include "code\datums\outfits\antagonist\pirate.dm" #include "code\datums\outfits\equipment\backpacks.dm" #include "code\datums\outfits\jobs\_defines.dm" #include "code\datums\outfits\jobs\church.dm" @@ -552,6 +555,8 @@ #include "code\game\antagonist\outer\blitzshell\blitzshell.dm" #include "code\game\antagonist\outer\mercenary\merc_faction.dm" #include "code\game\antagonist\outer\mercenary\mercenary.dm" +#include "code\game\antagonist\outer\pirate\pirate.dm" +#include "code\game\antagonist\outer\pirate\pirate_faction.dm" #include "code\game\antagonist\station\changeling.dm" #include "code\game\antagonist\station\contractor.dm" #include "code\game\antagonist\station\inquisitor.dm" @@ -626,6 +631,7 @@ #include "code\game\gamemodes\roleset\simple.dm" #include "code\game\gamemodes\roleset\faction\excelsior.dm" #include "code\game\gamemodes\roleset\faction\mercenary.dm" +#include "code\game\gamemodes\roleset\faction\pirate.dm" #include "code\game\gamemodes\roleset\faction\roleset_faction.dm" #include "code\game\gamemodes\storytellers\chronicler.dm" #include "code\game\gamemodes\storytellers\guide.dm" diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm index bca4879bf35..753283df01e 100644 --- a/code/__DEFINES/gamemode.dm +++ b/code/__DEFINES/gamemode.dm @@ -48,6 +48,7 @@ #define ROLE_DEATHSQUAD "deathsquad" #define ROLE_ARTIST "artist" #define ROLE_MERCENARY "mercenary" +#define ROLE_PIRATE "pirate" #define ROLE_CARRION "carrion" #define ROLE_MONKEY "monkey" #define ROLE_MALFUNCTION "malf" @@ -65,6 +66,7 @@ #define FACTION_EXCELSIOR "excelsior" #define FACTION_BORERS "borers" #define FACTION_SERBS "serbians" +#define FACTION_PIRATES "pirates" #define FACTION_NEOTHEOLOGY "neotheologists" #define ROLES_CONTRACT_COMPLETE list(ROLE_CONTRACTOR,ROLE_CARRION) // Blitz not included diff --git a/code/__DEFINES/items_clothing.dm b/code/__DEFINES/items_clothing.dm index eb40a7b7118..553c5614680 100644 --- a/code/__DEFINES/items_clothing.dm +++ b/code/__DEFINES/items_clothing.dm @@ -81,6 +81,7 @@ #define DRAG_AND_DROP_UNEQUIP 0x80 // Allow you put intems in hands with drag and drop #define EQUIP_SOUNDS 0x100// Play sound when equipped/unequipped #define ABSTRACT 0x200//For items that don't really exist. Can't be put on tables or interacted with. +#define PIRATE_BASE 0x400//For items spawned in pirate base so that they are worthless if placed in the loot chests (otherwise pirates can loot their own gear) // Flags for pass_flags. #define PASSTABLE 0x1 diff --git a/code/controllers/communications.dm b/code/controllers/communications.dm index 160e0999279..392a4e884f3 100644 --- a/code/controllers/communications.dm +++ b/code/controllers/communications.dm @@ -109,6 +109,7 @@ var/const/COMM_FREQ = 1353 var/const/AI_FREQ = 1343 var/const/DTH_FREQ = 1341 var/const/SYND_FREQ = 1213 +var/const/YARR_FREQ = 1220 // department channels var/const/PUB_FREQ = 1459 @@ -134,6 +135,7 @@ var/list/radiochannels = list( "Security" = SEC_FREQ, "Special Ops" = DTH_FREQ, "Mercenary" = SYND_FREQ, + "Pirate" = YARR_FREQ, "Supply" = SUP_FREQ, "NT Voice" = NT_FREQ, "Service" = SRV_FREQ, @@ -146,7 +148,7 @@ var/list/radiochannels = list( var/list/CENT_FREQS = list(DTH_FREQ) // Antag channels, i.e. Syndicate -var/list/ANTAG_FREQS = list(SYND_FREQ) +var/list/ANTAG_FREQS = list(SYND_FREQ, YARR_FREQ) //Department channels, arranged lexically var/list/DEPT_FREQS = list(AI_FREQ, COMM_FREQ, ENG_FREQ, MED_FREQ, NT_FREQ, SEC_FREQ, SCI_FREQ, SRV_FREQ, SUP_FREQ) diff --git a/code/datums/objective/pirate_plunder.dm b/code/datums/objective/pirate_plunder.dm new file mode 100644 index 00000000000..3c9d9daed4c --- /dev/null +++ b/code/datums/objective/pirate_plunder.dm @@ -0,0 +1,111 @@ +//Possible mission states +#define PLUNDER_STATUS_SETUP 0 //Mercs are still at their base getting equipped +#define PLUNDER_STATUS_IN_PROGRESS 1 //Mission has started, timer is ticking +#define PLUNDER_STATUS_ABORTED 2 //Time limit expired, mission failed +#define PLUNDER_STATUS_POSTGAME 3 //Mercs returned to their base. They get half an hour to roleplay and debrief# +#define PLUNDER_STATUS_ENDED 4 //All pirates have been despawned + +/datum/objective/timed/pirate + explanation_text = "Return to your ship and withdraw to base within X minutes." + var/mission_timer = 45 MINUTES + var/mission_status = PLUNDER_STATUS_SETUP + var/ended = FALSE + +/datum/objective/timed/pirate/check_completion() + if (failed) + return FALSE + + var/datum/shuttle/autodock/multi/antag/pirate/MS = SSshuttle.get_shuttle("Pirate") + + if (!MS) + //Shuttle was destroyed? + return FALSE + + + if (MS.current_location != MS.home_waypoint && MS.next_location != MS.home_waypoint) + //The shuttle still near Eris, fail + //This will succeed as long as they're enroute away from eris + return FALSE + + return TRUE + + +/datum/objective/timed/pirate/update_explanation() + explanation_text = "Return to your ship and withdraw to base within [round(mission_timer / (1 MINUTE), 1)] minutes." + +/datum/objective/timed/pirate/get_panel_entry() + return "Withdraw to base within [round(mission_timer / (1 MINUTE), 1)] minutes. Time remaining: [time2text(mission_timer, "mm:ss")]." + +/datum/objective/timed/pirate/get_info() + return "Time remaining at arrival: [time2text(mission_timer, "mm:ss")]." + +/datum/objective/timed/pirate/proc/start_mission() + START_PROCESSING(SSobj, src) + mission_status = PLUNDER_STATUS_IN_PROGRESS + +//The faction datum processes to tick down the mission timer +/datum/objective/timed/pirate/Process() + mission_timer -= 1 SECONDS + if (!ended && mission_timer <= 0) + end_mission() + + /* + The timer keeps ticking even after its ended because later i plan to extend this to let them hang + around the pirate base for up to half an hour and then be despawned so the base can be reset + */ + + + +//The mission ends when the pirates return to base or their time limit expires +/datum/objective/timed/pirate/proc/end_mission() + ended = TRUE + if (!check_completion()) + abort_mission() + mission_status = PLUNDER_STATUS_ABORTED + else + for (var/datum/objective/O in owner_faction.objectives) + if (O.check_completion()) + O.completed = TRUE + mission_status = PLUNDER_STATUS_POSTGAME + + // Not going back to Eris once mission is over + var/datum/shuttle/autodock/multi/antag/pirate/MS = SSshuttle.get_shuttle("Pirate") + if(MS) + MS.lock_shuttle() + + //This is one of the few times a world << call is actually intended functionality. + //This is not a debug message, it outputs the result of the mission, it should remain in + to_chat(world, owner_faction.print_success()) + + + +//This is called if the pirates' time limit expires while they're not at their base. Mission failure +/datum/objective/timed/pirate/proc/abort_mission() + + //First of all, every pirate left on eris is executed by a little bomb in their skull + for (var/datum/antagonist/A in owner_faction.members) + if (!A || !A.owner) + continue + + var/mob/living/carbon/human/H = A.owner.current + if (!H) + continue + + if (isNotStationLevel(H.z)) + continue + + var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD) + affecting.take_damage(9999) //Headgib. Very dead + + + + //Secondly, fail all mission objectives + for (var/datum/objective/O in owner_faction.objectives) + O.failed = TRUE + + /* + // Thirdly, the pirate ship selfdestructs + var/list/atoms = get_area_contents(/area/shuttle/pirate) + for (var/a in atoms) + qdel(a) + */ diff --git a/code/datums/objective/plunder.dm b/code/datums/objective/plunder.dm new file mode 100644 index 00000000000..0848bda3de7 --- /dev/null +++ b/code/datums/objective/plunder.dm @@ -0,0 +1,58 @@ +/datum/objective/plunder + target_amount = 15000 // Cumulated value of loot to plunder + unique = TRUE + +/datum/objective/plunder/New(datum/antagonist/new_owner, datum/mind/_target) + ..() + if(owner_faction) + target_amount *= LAZYLEN(owner_faction.members) + update_explanation() + +/datum/objective/plunder/check_completion() + if (failed) + return FALSE + if(owner && (!owner.current || owner.current.stat == DEAD)) + return FALSE + + return (get_loot_value() < target_amount) ? FALSE : TRUE + +/datum/objective/plunder/proc/get_loot_value() + var/list/contents = list() + + // Get inventory of the faction + if (owner_faction) + contents.Add(owner_faction.get_inventory()) + + var/cumulated_amount = 0 + + // Check cumulated loot value + for(var/atom/movable/A in contents) + if(isitem(A)) + var/obj/item/I = A + if(I.item_flags & PIRATE_BASE) // Item spawned in pirate base are worthless for pirates + continue + cumulated_amount += SStrade.get_price(A, TRUE) + + return cumulated_amount + +/datum/objective/plunder/update_explanation() + explanation_text = "Plunder loot with a cumulated value of [target_amount] credits. Loot must be stored on the raid shuttle in a loot crate to be considered toward your objective." + +/datum/objective/plunder/get_panel_entry() + return "Plunder loot with a cumulated value of [target_amount] credits. \ + Value of loot: [get_loot_value()] credits." + +/datum/objective/plunder/get_info() + return "Value of loot: [get_loot_value()] credits." + +/datum/objective/plunder/Topic(href, href_list) + if(..()) + return TRUE + if(href_list["set_target"]) + var/new_target = input("Input target number:", "Value of loot to plunder", target_amount) as num|null + if(new_target < 1) + return + else + target_amount = new_target + update_explanation() + antag.antagonist_panel() diff --git a/code/datums/outfits/antagonist/pirate.dm b/code/datums/outfits/antagonist/pirate.dm new file mode 100644 index 00000000000..e38379b7ca3 --- /dev/null +++ b/code/datums/outfits/antagonist/pirate.dm @@ -0,0 +1,29 @@ +/decl/hierarchy/outfit/antagonist/pirate + + hierarchy_type = /decl/hierarchy/outfit/antagonist/pirate + + uniform = /obj/item/clothing/under/pirate + l_ear = /obj/item/device/radio/headset/pirates + shoes = /obj/item/clothing/shoes/jackboots + head = /obj/item/clothing/head/bandana + pda_slot = slot_belt + pda_type = /obj/item/modular_computer/pda + +//The outfit that mercs spawn in. They get their armor and weapons from the merc base +/decl/hierarchy/outfit/antagonist/pirate/casual + name = "Pirate garb" + +//He gets a snazzy beret +/decl/hierarchy/outfit/antagonist/pirate/commander + name = "Pirate Quartermaster garb" + glasses = /obj/item/clothing/glasses/eyepatch + head = /obj/item/clothing/head/pirate + +//This outfit is just for admin fun. Spawns them fully equipped +//Actual pirates equip themselves by picking up their garb from their base +/decl/hierarchy/outfit/antagonist/pirate/equipped + name = "Pirate plundering gear" + + suit = /obj/item/clothing/suit/pirate + back = /obj/item/storage/backpack/satchel + belt = /obj/item/melee/energy/sword/pirate diff --git a/code/game/antagonist/antagonist_equip.dm b/code/game/antagonist/antagonist_equip.dm index 58438567647..63e5c2e87b8 100644 --- a/code/game/antagonist/antagonist_equip.dm +++ b/code/game/antagonist/antagonist_equip.dm @@ -65,6 +65,8 @@ if(freq == SYND_FREQ) R = new/obj/item/device/radio/headset/syndicate(H) + else if(freq == YARR_FREQ) + R = new/obj/item/device/radio/headset/pirates(H) else R = new/obj/item/device/radio/headset(H) diff --git a/code/game/antagonist/antagonist_factions.dm b/code/game/antagonist/antagonist_factions.dm index 5f3664a1722..2939b168316 100755 --- a/code/game/antagonist/antagonist_factions.dm +++ b/code/game/antagonist/antagonist_factions.dm @@ -189,6 +189,7 @@ for(var/datum/objective/O in objectives) text += "
Objective [num]: [O.explanation_text] " + text += "[O.get_info()] " if(O.check_completion()) text += "Success!" else diff --git a/code/game/antagonist/outer/pirate/pirate.dm b/code/game/antagonist/outer/pirate/pirate.dm new file mode 100644 index 00000000000..75bd0bdd411 --- /dev/null +++ b/code/game/antagonist/outer/pirate/pirate.dm @@ -0,0 +1,142 @@ +#define WELCOME_PIRATES "You are a pirate, part of a ragged crew of space scavengers. You are currently aboard your base preparing for a raid to plunder the CEV Eris.
\ +
\ + In your base you will find your armoury full of weapon crates, tools and utilities. It is advised that you take a pistol and a rifle as well as the energy cutlass and bulletproof vest that are in your personnal locker.
\ + Once you have your basic gear, you may also wish to take along additional gear to support your team, like demolition tools, grenades or medkits.
\ +
\ + Discuss your role with other crew members and decide of an approach plan to accomplish your mission. Search the base and load up everything onto your ship which may be useful, you will not be able to easily return here once you depart.
\ + When ready, use the console aboard your shuttle to depart for Eris. Travelling will take several minutes, and you will be detected before you even arrive, stealth is not an option. Once you arrive, you have a time limit to complete your mission.
\ + Your crew captain will not accept failure or desertion so you better bring back enough loot to satisfy his greed." + +/datum/antagonist/pirate + id = ROLE_PIRATE + bantype = ROLE_PIRATE + faction_id = FACTION_PIRATES + role_text = "Pirate" + welcome_text = WELCOME_PIRATES + antaghud_indicator = "hudoperative" + landmark_id = "pirate-spawn" + outer = TRUE + + default_access = list(access_pirate, // This access governs their ship and base + access_external_airlocks, + access_maint_tunnels) // Pirates get maintenance access on eris, because being an antag without it is hell + id_type = /obj/item/card/id/pirate // They got forged vagabond IDs + + appearance_editor = FALSE + + possible_objectives = list() + survive_objective = null + + // A bunch of rowdy pirates should be somewhat equivalent to roundstart IH operative + stat_modifiers = list( + STAT_ROB = 25, + STAT_TGH = 20, + STAT_VIG = 25, + STAT_BIO = 10, + STAT_MEC = 10, + STAT_COG = 10 + ) + + perks = list(PERK_SURVIVOR) + +/datum/antagonist/pirate/equip() + var/mob/living/L = owner.current + + // Put on the fatigues. Armor not included, they equip that manually from the merc base + var/decl/hierarchy/outfit/O = outfit_by_type(/decl/hierarchy/outfit/antagonist/pirate/casual) + O.equip(L) + + // Set their language, This also adds it to their list + L.set_default_language(LANGUAGE_COMMON) + + // And we'll give them a random serbian name to start off with + var/datum/language/lang = all_languages[LANGUAGE_COMMON] + lang.set_random_name(L) + + // The missing part was antag's stats! + for(var/name in stat_modifiers) + L.stats.changeStat(name, stat_modifiers[name]) + + for(var/perk in perks) + L.stats.addPerk(perk) + + create_id("Pirate") + ..() + +// PIRATE ID + +/obj/item/card/id/pirate + icon_state = "syndicate" + +/obj/item/card/id/pirate/Initialize(mapload) + . = ..() + access = list(access_pirate, //This access governs their ship and base + access_external_airlocks, + access_maint_tunnels) + +// PIRATE LOCKER + +/obj/structure/closet/secure_closet/pirate + name = "pirate locker" + req_access = list(access_pirate) + icon_state = "cabinet" + icon_lock = "cabinet" + +/obj/structure/closet/secure_closet/pirate/populate_contents() + new /obj/item/clothing/under/pirate(src) + new /obj/item/device/radio/headset/pirates(src) + new /obj/item/clothing/shoes/jackboots(src) + new /obj/item/clothing/head/bandana(src) + new /obj/item/clothing/suit/armor/bulletproof(src) + new /obj/item/storage/backpack/satchel(src) + new /obj/item/melee/energy/sword/pirate(src) + +// PIRATE LOOT CRATE + +/obj/structure/closet/crate/pirate + name = "loot crate" + desc = "A rectangular steel crate to store your pricy and ethically obtained loot." + +// LOOT CHECKER + +#define CHECKER_COOLDOWN 1 MINUTE + +/obj/item/device/loot_checker + name = "loot value checker" + desc = "Used to check the total value of plundered loot." + icon_state = "voice0" + item_state = "flashbang" + w_class = ITEM_SIZE_TINY + matter = list(MATERIAL_PLASTIC = 2, MATERIAL_GLASS = 1, MATERIAL_STEEL = 2) + flags = CONDUCT + anchored = TRUE + + var/last_use = - CHECKER_COOLDOWN + +/obj/item/device/loot_checker/attack_hand(mob/user) + if(world.time > last_use + CHECKER_COOLDOWN) + var/cumulated_amount = check_loot_value() + if(cumulated_amount) + audible_message(SPAN_WARNING("[src] beeps: 'LOOT VALUE: [cumulated_amount] credits.'")) + else + audible_message(SPAN_WARNING("[src] beeps: 'ERROR: Unable to compute loot value.'")) + last_use = world.time + else + to_chat(user, SPAN_NOTICE("The loot value checker has [round((last_use + CHECKER_COOLDOWN - world.time) / (1 SECOND))] seconds of cooldown remaining.")) + +/obj/item/device/loot_checker/proc/check_loot_value() + var/cumulated_amount = 0 + + // Check cumulated loot value + for(var/obj/structure/closet/crate/pirate/P in get_area_contents(/area/shuttle/pirate)) + var/turf/T = get_turf(P) + for(var/atom/movable/A in T.get_recursive_contents()) + if(isitem(A)) + var/obj/item/I = A + if(I.item_flags & PIRATE_BASE) // Item spawned in pirate base are worthless for pirates + continue + cumulated_amount += SStrade.get_price(A, TRUE) + + return cumulated_amount + +#undef CHECKER_COOLDOWN diff --git a/code/game/antagonist/outer/pirate/pirate_faction.dm b/code/game/antagonist/outer/pirate/pirate_faction.dm new file mode 100644 index 00000000000..3878707d884 --- /dev/null +++ b/code/game/antagonist/outer/pirate/pirate_faction.dm @@ -0,0 +1,52 @@ +/datum/faction/pirate + id = FACTION_PIRATES + name = "Pirates" + antag = "pirate" + antag_plural = "pirates" + welcome_text = WELCOME_PIRATES + + hud_indicator = "pirate" + + possible_antags = list(ROLE_PIRATE) + + faction_invisible = FALSE + + var/objectives_num + var/list/possible_objectives = list( + /datum/objective/plunder = 10) + var/objective_quantity = 2 + +/datum/faction/pirate/create_objectives() + make_base_items_worthless() + + objectives.Cut() + pick_objectives(src, possible_objectives, objective_quantity) + + new /datum/objective/timed/pirate(src) + + ..() + +/datum/faction/pirate/add_leader(var/datum/antagonist/member, var/announce = TRUE) + .=..() + if (.) + // Put the commander outfit on + var/decl/hierarchy/outfit/O = outfit_by_type(/decl/hierarchy/outfit/antagonist/pirate/commander) + O.equip(member.owner.current, OUTFIT_ADJUSTMENT_NO_RESET) + + member.create_id("Pirate Quartermaster") + +// Special inventory proc for pirates. Includes the content of their loot crates. +/datum/faction/pirate/get_inventory() + var/list/contents = list() + for(var/obj/structure/closet/crate/pirate/P in get_area_contents(/area/shuttle/pirate)) + var/turf/T = get_turf(P) + contents |= T.get_recursive_contents() + + return contents + +// Make all stuff in pirate base worthless so that it cannot be used as loot in the loot crates +/datum/faction/pirate/proc/make_base_items_worthless() + for(var/obj/item/I in get_area_contents(/area/centcom/pirate_base)) + I.item_flags |= PIRATE_BASE + +#undef WELCOME_PIRATES diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 18ccfd41d06..c11d07b09e5 100644 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -247,6 +247,10 @@ area/space/atmosalert() name = "\improper Mercenary Ship" flags = AREA_FLAG_RAD_SHIELDED +/area/shuttle/pirate + name = "\improper Pirate Ship" + flags = AREA_FLAG_RAD_SHIELDED + /area/shuttle/syndicate_elite/mothership icon_state = "shuttlered" @@ -358,6 +362,13 @@ area/space/atmosalert() dynamic_lighting = 0 vessel = "syndicate mothership" +/area/centcom/pirate_base + name = "\improper Pirate Base" + icon_state = "syndie-ship" + requires_power = 0 + dynamic_lighting = 0 + vessel = "pirate mothership" + /area/centcom/raider_base name = "\improper Raider Base" icon_state = "syndie-control" diff --git a/code/game/gamemodes/events/weather/rad_storm.dm b/code/game/gamemodes/events/weather/rad_storm.dm index 3863b0b22ed..968d8cf3077 100644 --- a/code/game/gamemodes/events/weather/rad_storm.dm +++ b/code/game/gamemodes/events/weather/rad_storm.dm @@ -22,6 +22,6 @@ /area/eris/crew_quarters/sleep/cryo, /area/eris/security/disposal, /area/eris/security/maintpost, /area/eris/rnd/anomalisolone, \ /area/eris/rnd/anomalisoltwo, /area/eris/rnd/anomalisolthree, /area/eris/rnd/server, \ /area/outpost/blacksite/small, /area/outpost/blacksite/medium, /area/outpost/blacksite/large, \ - /area/asteroid/cave) + /area/asteroid/cave, /area/shuttle/pirate) immunity_type = "rad" diff --git a/code/game/gamemodes/roleset/faction/pirate.dm b/code/game/gamemodes/roleset/faction/pirate.dm new file mode 100644 index 00000000000..0a00fb7b329 --- /dev/null +++ b/code/game/gamemodes/roleset/faction/pirate.dm @@ -0,0 +1,19 @@ +/datum/storyevent/roleset/faction/pirate + id = "pirate" + name = "pirates" + role_id = ROLE_PIRATE + weight = 0.6 + occurrences_max = 1 + req_crew = 5 + tags = list(TAG_DESTRUCTIVE, TAG_NEGATIVE) + + // Until we code some way to reset the pirate base, can't allow this to happen more than once per round + + min_quantity = 2 // Don't fire unless we have at least 2 candidates in the pool + base_quantity = 1 + scaling_threshold = 4 + + leaders = 1 + + faction_id = FACTION_PIRATES + faction_type = /datum/faction/pirate diff --git a/code/game/jobs/access_datum.dm b/code/game/jobs/access_datum.dm index 036295e568a..1e54f37eb5c 100644 --- a/code/game/jobs/access_datum.dm +++ b/code/game/jobs/access_datum.dm @@ -559,6 +559,10 @@ id = access_mercenary access_type = ACCESS_TYPE_SYNDICATE +/var/const/access_pirate = 145 //Pirate ship and base +/datum/access/pirate + id = access_pirate + access_type = ACCESS_TYPE_SYNDICATE /var/const/access_syndicate = 140//General Syndicate Access /datum/access/syndicate diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm index 522413f54a7..ce8f1326cdf 100644 --- a/code/game/machinery/telecomms/broadcaster.dm +++ b/code/game/machinery/telecomms/broadcaster.dm @@ -583,6 +583,8 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept blackbox.msg_deathsquad += blackbox_msg if(SYND_FREQ) blackbox.msg_syndicate += blackbox_msg + if(YARR_FREQ) + blackbox.msg_pirate += blackbox_msg if(SUP_FREQ) blackbox.msg_cargo += blackbox_msg if(SRV_FREQ) diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm index 5b488375133..42a730291b4 100644 --- a/code/game/objects/items/devices/radio/encryptionkey.dm +++ b/code/game/objects/items/devices/radio/encryptionkey.dm @@ -14,6 +14,7 @@ var/translate_hive = FALSE var/syndie = FALSE var/merc = FALSE + var/pirate = FALSE var/list/channels = list() /obj/item/device/encryptionkey/attackby(obj/item/W, mob/user) @@ -29,6 +30,11 @@ channels = list("Mercenary" = 1) merc = TRUE +/obj/item/device/encryptionkey/pirates + icon_state = "cypherkey" + channels = list("Pirate" = 1) + pirate = TRUE + /obj/item/device/encryptionkey/binary icon_state = "cypherkey" translate_binary = TRUE diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index 13920b523ad..544c45f52b9 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -76,6 +76,11 @@ ks1type = /obj/item/device/encryptionkey/mercenaries spawn_blacklisted = TRUE +/obj/item/device/radio/headset/pirates + origin_tech = list(TECH_COVERT = 2) + ks1type = /obj/item/device/encryptionkey/pirates + spawn_blacklisted = TRUE + /obj/item/device/radio/headset/binary origin_tech = list(TECH_COVERT = 3) ks1type = /obj/item/device/encryptionkey/binary @@ -272,6 +277,7 @@ src.translate_hive = FALSE src.syndie = FALSE src.merc = FALSE + src.pirate = FALSE if(keyslot1) for(var/ch_name in keyslot1.channels) @@ -292,6 +298,9 @@ if(keyslot1.merc) src.merc = TRUE + if(keyslot1.pirate) + src.pirate = TRUE + if(keyslot2) for(var/ch_name in keyslot2.channels) if(ch_name in src.channels) @@ -311,6 +320,9 @@ if(keyslot2.merc) src.merc = TRUE + if(keyslot2.pirate) + src.pirate = TRUE + for (var/ch_name in channels) secure_radio_connections[ch_name] = SSradio.add_object(src, radiochannels[ch_name], RADIO_CHAT) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 5e013572cb9..fde65e573e1 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -51,6 +51,7 @@ var/global/list/default_medbay_channels = list( var/subspace_transmission = 0 var/syndie = FALSE//Holder to see if it's a syndicate encrypted radio var/merc = FALSE //Holder to see if it's a mercenary encrypted radio + var/pirate = FALSE //Holder to see if it's a pirate encrypted radio var/const/FREQ_LISTENING = 1 var/list/internal_channels @@ -515,7 +516,7 @@ var/global/list/default_medbay_channels = list( return -1 if(freq in ANTAG_FREQS) - if(!syndie && !merc)//Checks to see if it's allowed on that frequency, based on the encryption keys + if(!syndie && !merc && !pirate)//Checks to see if it's allowed on that frequency, based on the encryption keys return -1 var/can_recieve = (freq == frequency) diff --git a/code/game/objects/landmarks/storyevent.dm b/code/game/objects/landmarks/storyevent.dm index 9f2bc6ae94c..21cea84e88f 100644 --- a/code/game/objects/landmarks/storyevent.dm +++ b/code/game/objects/landmarks/storyevent.dm @@ -35,6 +35,11 @@ icon_state = "spy-green" navigation = "This marks Serbia, it is our land." +/obj/landmark/storyevent/pirate_spawn + name = "pirate-spawn" + icon_state = "spy-green" + navigation = "This marks Tortuga, our base of operation." + /obj/landmark/storyevent/potential_unique_oddity_spawn name = "special-oddity-spawn" icon_state = "spy-cyan" diff --git a/code/modules/cargo/exports/large_objects.dm b/code/modules/cargo/exports/large_objects.dm index 717e9294e2a..1443ecd6cca 100644 --- a/code/modules/cargo/exports/large_objects.dm +++ b/code/modules/cargo/exports/large_objects.dm @@ -44,6 +44,10 @@ contents_cost = 700 export_types = list(/obj/structure/reagent_dispensers/beerkeg) +/datum/export/large/reagent_dispenser/rum + unit_name = "rum keg" + contents_cost = 700 + export_types = list(/obj/structure/reagent_dispensers/rumkeg) // Heavy engineering equipment. Singulo/Tesla parts mostly. diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 368406f7a10..893ff7c956d 100755 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -11,6 +11,7 @@ var/list/department_radio_keys = list( "s" = "Security", "S" = "Security", "w" = "whisper", "W" = "whisper", "y" = "Mercenary", "Y" = "Mercenary", + "x" = "Pirate", "X" = "Pirate", "u" = "Supply", "U" = "Supply", "v" = "Service", "V" = "Service", "p" = "AI Private", "P" = "AI Private", @@ -27,6 +28,7 @@ var/list/department_radio_keys = list( "ы" = "Security", "Ы" = "Security", "ц" = "whisper", "Ц" = "whisper", "н" = "Mercenary", "Н" = "Mercenary", + "ч" = "Pirate", "Ч" = "Pirate", "г" = "Supply", "Г" = "Supply", "м" = "Service", "М" = "Service", "з" = "AI Private", "З" = "AI Private", diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index c800fff136a..60abfbb2d79 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -268,6 +268,17 @@ contents_cost = 700 spawn_blacklisted = TRUE +/obj/structure/reagent_dispensers/rumkeg + name = "rum keg" + desc = "A rum keg" + icon_state = "beertankTEMP" + amount_per_transfer_from_this = 10 + volume = 1000 + starting_reagent = "rum" + price_tag = 50 + contents_cost = 700 + spawn_blacklisted = TRUE + /obj/structure/reagent_dispensers/coolanttank name = "coolant tank" desc = "A tank of industrial coolant" diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm index 98c289d7c92..63ca207116f 100644 --- a/code/modules/research/message_server.dm +++ b/code/modules/research/message_server.dm @@ -188,6 +188,7 @@ var/obj/machinery/blackbox_recorder/blackbox var/list/msg_security = list() var/list/msg_deathsquad = list() var/list/msg_syndicate = list() + var/list/msg_pirate = list() var/list/msg_cargo = list() var/list/msg_service = list() var/list/msg_nt = list() @@ -214,6 +215,7 @@ var/obj/machinery/blackbox_recorder/blackbox BR.msg_security = msg_security BR.msg_deathsquad = msg_deathsquad BR.msg_syndicate = msg_syndicate + BR.msg_pirate = msg_pirate BR.msg_cargo = msg_cargo BR.msg_service = msg_service BR.msg_nt = msg_nt diff --git a/code/modules/shuttles/antagonist.dm b/code/modules/shuttles/antagonist.dm index d87d95ec960..cbc3cb648fb 100644 --- a/code/modules/shuttles/antagonist.dm +++ b/code/modules/shuttles/antagonist.dm @@ -8,6 +8,11 @@ req_access = list(access_mercenary) shuttle_tag = "Mercenary" +/obj/machinery/computer/shuttle_control/multi/pirate + name = "pirate shuttle control console" + req_access = list(access_pirate) + shuttle_tag = "Pirate" + /obj/machinery/computer/shuttle_control/multi/rescue name = "rescue shuttle control console" req_access = list(access_cent_specops) diff --git a/icons/mob/mob.dmi b/icons/mob/mob.dmi index 5c861a3c6a328d99068a038c624316f8c8ecb784..65b908869e26823f12316c864ed47bdd91f956a6 100644 GIT binary patch delta 4820 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