diff --git a/code/__DEFINES/factions.dm b/code/__DEFINES/factions.dm new file mode 100644 index 000000000000..73d49539ecbb --- /dev/null +++ b/code/__DEFINES/factions.dm @@ -0,0 +1,174 @@ +//FACTION NAMES +#define FACTION_NEUTRAL "neutral" +//USCM +#define FACTION_USCM "uscm" +#define FACTION_MARINE "cm" +#define FACTION_CMB "cmb" +#define FACTION_MARSOC "msoc" +//CONTRACTOR +#define FACTION_CONTRACTOR "contractor" +//WY +#define FACTION_WY "wey_yu" +#define FACTION_PMC "pmc" +#define FACTION_WY_DEATHSQUAD "wy_death_sqaud" +//UPP +#define FACTION_UPP "upp" +//CLF +#define FACTION_CLF "clf" +//COLON +#define FACTION_COLONIST "colonist" +//OTHER +#define FACTION_RESS "ress" +#define FACTION_TWE "twe" +#define FACTION_MERCENARY "mercenary" +#define FACTION_FREELANCER "freelancer" +#define FACTION_HEFA "hefa_order" +#define FACTION_DUTCH "dutch's_dozen" +#define FACTION_PIRATE "pirate" +#define FACTION_GLADIATOR "gladiator" +#define FACTION_PIZZA "pizza_delivery" +#define FACTION_SOUTO "souto" +//ZOMBIE +#define FACTION_ZOMBIE "zombie" +//YAUTJA +#define FACTION_YAUTJA "yautja" +//XENOS +#define FACTION_XENOMORPH "xeno" +#define FACTION_XENOMORPH_NORMAL "xenomorph" +#define FACTION_XENOMORPH_CORRUPTED "corrupted_xenomoprh" +#define FACTION_XENOMORPH_ALPHA "alpha_xenomorph" +#define FACTION_XENOMORPH_BRAVO "bravo_xenomorph" +#define FACTION_XENOMORPH_CHARLIE "charlie_xenomorph" +#define FACTION_XENOMORPH_DELTA "delta_xenomorph" +#define FACTION_XENOMORPH_FERAL "feral_xenomorph" +#define FACTION_XENOMORPH_FORSAKEN "forsaken_xenomorph" +#define FACTION_XENOMORPH_TAMED "tamed_xenomorph" +#define FACTION_XENOMORPH_MUTATED "mutated_xenomorph" +#define FACTION_XENOMORPH_YAUTJA "yautja_xenomorph" +#define FACTION_XENOMORPH_RENEGADE "renegade_xenomorph" + +#define FACTION_LIST_MARINE list(FACTION_USCM, FACTION_MARINE, FACTION_CMB, FACTION_MARSOC) +#define FACTION_LIST_WY list(FACTION_WY, FACTION_PMC, FACTION_WY_DEATHSQUAD) +#define FACTION_LIST_HUMANOID list(FACTION_NEUTRAL, FACTION_CONTRACTOR, FACTION_CLF, FACTION_UPP, FACTION_FREELANCER, FACTION_COLONIST, FACTION_MERCENARY, FACTION_DUTCH, FACTION_HEFA, FACTION_GLADIATOR, FACTION_PIRATE, FACTION_PIZZA, FACTION_SOUTO, FACTION_YAUTJA) + FACTION_LIST_MARINE + FACTION_LIST_WY +#define FACTION_LIST_XENOMORPH list(FACTION_XENOMORPH_NORMAL, FACTION_XENOMORPH_CORRUPTED, FACTION_XENOMORPH_ALPHA, FACTION_XENOMORPH_BRAVO, FACTION_XENOMORPH_CHARLIE, FACTION_XENOMORPH_DELTA, FACTION_XENOMORPH_FERAL, FACTION_XENOMORPH_FORSAKEN, FACTION_XENOMORPH_TAMED, FACTION_XENOMORPH_MUTATED, FACTION_XENOMORPH_YAUTJA, FACTION_XENOMORPH_RENEGADE) +#define FACTION_LIST_ALL FACTION_LIST_HUMANOID + FACTION_LIST_XENOMORPH +/// This is factions handle defcons +#define FACTION_LIST_DEFCONED list(FACTION_USCM, FACTION_MARINE, FACTION_UPP) + +//FACTIONS RELATIONS +#define RELATIONS_FACTION_NEUTRAL list(FACTION_USCM = RELATIONS_NEUTRAL, FACTION_WY = RELATIONS_NEUTRAL, FACTION_UPP = RELATIONS_NEUTRAL, FACTION_CLF = RELATIONS_NEUTRAL, FACTION_COLONIST = RELATIONS_NEUTRAL, FACTION_RESS = RELATIONS_NEUTRAL, FACTION_TWE = RELATIONS_NEUTRAL, FACTION_MERCENARY = RELATIONS_NEUTRAL, FACTION_FREELANCER = RELATIONS_NEUTRAL, FACTION_THREEWE = RELATIONS_NEUTRAL) +#define RELATIONS_FACTION_USCM list(FACTION_WY = RELATIONS_FRIENDLY, FACTION_UPP = RELATIONS_HOSTILE, FACTION_CLF = RELATIONS_HOSTILE, FACTION_COLONIST = RELATIONS_NEUTRAL, FACTION_RESS = RELATIONS_FRIENDLY, FACTION_TWE = RELATIONS_FRIENDLY, FACTION_MERCENARY = RELATIONS_NEUTRAL, FACTION_FREELANCER = RELATIONS_NEUTRAL, FACTION_THREEWE = RELATIONS_TENSE, FACTION_NEUTRAL = RELATIONS_NEUTRAL) +#define RELATIONS_FACTION_WY list(FACTION_USCM = RELATIONS_FRIENDLY, FACTION_UPP = RELATIONS_NEUTRAL, FACTION_CLF = RELATIONS_HOSTILE, FACTION_COLONIST = RELATIONS_NEUTRAL, FACTION_RESS = RELATIONS_FRIENDLY, FACTION_TWE = RELATIONS_HOSTILE, FACTION_MERCENARY = RELATIONS_NEUTRAL, FACTION_FREELANCER = RELATIONS_NEUTRAL, FACTION_THREEWE = RELATIONS_NEUTRAL, FACTION_NEUTRAL = RELATIONS_NEUTRAL) +#define RELATIONS_FACTION_CLF list(FACTION_USCM = RELATIONS_HOSTILE, FACTION_WY = RELATIONS_NEUTRAL, FACTION_UPP = RELATIONS_HOSTILE, FACTION_COLONIST = RELATIONS_NEUTRAL, FACTION_RESS = RELATIONS_HOSTILE, FACTION_TWE = RELATIONS_HOSTILE, FACTION_MERCENARY = RELATIONS_HOSTILE, FACTION_FREELANCER = RELATIONS_HOSTILE, FACTION_THREEWE = RELATIONS_HOSTILE, FACTION_NEUTRAL = RELATIONS_NEUTRAL) +#define RELATIONS_FACTION_UPP list(FACTION_USCM = RELATIONS_HOSTILE, FACTION_WY = RELATIONS_FRIENDLY, FACTION_CLF = RELATIONS_HOSTILE, FACTION_COLONIST = RELATIONS_NEUTRAL, FACTION_RESS = RELATIONS_HOSTILE, FACTION_TWE = RELATIONS_FRIENDLY, FACTION_MERCENARY = RELATIONS_HOSTILE, FACTION_FREELANCER = RELATIONS_HOSTILE, FACTION_THREEWE = RELATIONS_HOSTILE, FACTION_NEUTRAL = RELATIONS_NEUTRAL) +#define RELATIONS_FACTION_XENOMORPH list(FACTION_XENOMORPH_NORMAL = RELATIONS_HOSTILE, FACTION_XENOMORPH_CORRUPTED = RELATIONS_HOSTILE, FACTION_XENOMORPH_ALPHA = RELATIONS_HOSTILE, FACTION_XENOMORPH_BRAVO = RELATIONS_HOSTILE, FACTION_XENOMORPH_CHARLIE = RELATIONS_HOSTILE, FACTION_XENOMORPH_DELTA = RELATIONS_HOSTILE, FACTION_XENOMORPH_FERAL = RELATIONS_HOSTILE, FACTION_XENOMORPH_FORSAKEN = RELATIONS_HOSTILE, FACTION_XENOMORPH_TAMED = RELATIONS_HOSTILE, FACTION_XENOMORPH_MUTATED = RELATIONS_HOSTILE, FACTION_XENOMORPH_YAUTJA = RELATIONS_HOSTILE, FACTION_XENOMORPH_RENEGADE = RELATIONS_HOSTILE, FACTION_NEUTRAL = RELATIONS_NEUTRAL) + +#define RELATIONS_MAP list(FACTION_NEUTRAL = null, FACTION_USCM = null, FACTION_MARINE = null, FACTION_CMB = null, FACTION_MARSOC = null, FACTION_CONTRACTOR = null, FACTION_WY = null, FACTION_PMC = null, FACTION_WY_DEATHSQUAD = null, FACTION_CLF = null, FACTION_UPP = null, FACTION_FREELANCER = null, FACTION_COLONIST = null, FACTION_MERCENARY = null, FACTION_DUTCH = null, FACTION_HEFA = null, FACTION_GLADIATOR = null, FACTION_PIRATE = null, FACTION_PIZZA = null, FACTION_SOUTO = null, FACTION_YAUTJA = null, FACTION_XENOMORPH_NORMAL = null, FACTION_XENOMORPH_CORRUPTED = null, FACTION_XENOMORPH_ALPHA = null, FACTION_XENOMORPH_BRAVO = null, FACTION_XENOMORPH_CHARLIE = null, FACTION_XENOMORPH_DELTA = null, FACTION_XENOMORPH_FERAL = null, FACTION_XENOMORPH_FORSAKEN = null, FACTION_XENOMORPH_TAMED = null, FACTION_XENOMORPH_MUTATED = null, FACTION_XENOMORPH_YAUTJA = null) +#define RELATIONS_MAP_HOSTILE list(FACTION_USCM = RELATIONS_HOSTILE, FACTION_WY = RELATIONS_HOSTILE, FACTION_UPP = RELATIONS_HOSTILE, FACTION_CLF = RELATIONS_HOSTILE, FACTION_COLONIST = RELATIONS_HOSTILE, FACTION_RESS = RELATIONS_HOSTILE, FACTION_TWE = RELATIONS_HOSTILE, FACTION_MERCENARY = RELATIONS_HOSTILE, FACTION_FREELANCER = RELATIONS_HOSTILE, FACTION_THREEWE = RELATIONS_HOSTILE, FACTION_XENOMORPH_NORMAL = RELATIONS_HOSTILE, FACTION_XENOMORPH_CORRUPTED = RELATIONS_HOSTILE, FACTION_XENOMORPH_ALPHA = RELATIONS_HOSTILE, FACTION_XENOMORPH_BRAVO = RELATIONS_HOSTILE, FACTION_XENOMORPH_CHARLIE = RELATIONS_HOSTILE, FACTION_XENOMORPH_DELTA = RELATIONS_HOSTILE, FACTION_XENOMORPH_FERAL = RELATIONS_HOSTILE, FACTION_XENOMORPH_FORSAKEN = RELATIONS_HOSTILE, FACTION_XENOMORPH_TAMED = RELATIONS_HOSTILE, FACTION_XENOMORPH_MUTATED = RELATIONS_HOSTILE, FACTION_XENOMORPH_YAUTJA = RELATIONS_HOSTILE, FACTION_NEUTRAL = RELATIONS_HOSTILE) + +#define RELATIONS_UNKNOWN null +#define RELATIONS_DISABLED list(0, 0) +#define RELATIONS_WAR list(1, 200) +#define RELATIONS_HOSTILE list(201, 400) +#define RELATIONS_TENSE list(401, 500) +#define RELATIONS_NEUTRAL list(501, 700) +#define RELATIONS_FRIENDLY list(701, 900) +#define RELATIONS_VERY_GOOD list(901, 1000) +#define RELATIONS_SELF 1100 +#define RELATIONS_MAX 1000 + +//FACTION TREES +#define SIDE_FACTION_NEUTRAL "NEUTRAL_T" +#define SIDE_FACTION_USCM "USCM_T" +#define SIDE_FACTION_WY "W-Y_T" +#define SIDE_FACTION_CLF "CLF_T" +#define SIDE_FACTION_UPP "UPP_T" +#define SIDE_FACTION_ZOMBIE "ZOMBIE_T" +#define SIDE_FACTION_YAUTJA "YAUTJA_T" +#define SIDE_FACTION_XENOMORPH "XENOMORPH_T" + +#define SIDE_ORGANICAL_DOM list(SIDE_FACTION_ZOMBIE, SIDE_FACTION_XENOMORPH) + +#define SITREP_INTERVAL 15 MINUTES + +//NAMES +#define NAME_FACTION_NEUTRAL "Neutral Faction" +//USCM +#define NAME_FACTION_USCM "United States Colonial Marines" +#define NAME_FACTION_MARINE "Colonial Marines" +#define NAME_FACTION_CMB "Colonial Marshal Bureau" +#define NAME_FACTION_MARSOC "Marine Special Operations Command" +//CONTRACTOR +#define NAME_FACTION_CONTRACTOR "Vanguard's Arrow Incorporated" +//WY +#define NAME_FACTION_WY "Weyland-Yutani" +#define NAME_FACTION_PMC "Private Military Company" +#define NAME_FACTION_WY_DEATHSQUAD "Corporate Commandos" +//UPP +#define NAME_FACTION_UPP "Union of Progressive Peoples" +//CLF +#define NAME_FACTION_CLF "Colonial Liberation Front" +//COLON +#define NAME_FACTION_COLONIST "Colonists" +//OTHER +#define NAME_FACTION_RESS "Royal Empire of the Shining Sun" +#define NAME_FACTION_TWE "Royal Marines Commando" +#define NAME_FACTION_MERCENARY "Mercenary Group" +#define NAME_FACTION_FREELANCER "Freelancer Mercenaries" +#define NAME_FACTION_HEFA "HEFA Knights" +#define NAME_FACTION_DUTCH "Dutch's Dozen" +#define NAME_FACTION_PIRATE "Pirates of Free Space" +#define NAME_FACTION_GLADIATOR "Gladiators" +#define NAME_FACTION_PIZZA "Pizza Galaxy" +#define NAME_FACTION_SOUTO "Souto Space" +#define NAME_FACTION_THREEWE "Three World Empire" +//ZOMBIE +#define NAME_FACTION_ZOMBIE "Zombie Horde" +//YAUTJA +#define NAME_FACTION_YAUTJA "Yautja Hanting Groop" +//XENOS +#define NAME_FACTION_XENOMORPH "Xenomorphs" +#define NAME_FACTION_XENOMORPH_NORMAL "Xenomorph Hive" +#define NAME_FACTION_XENOMORPH_CORRUPTED "Corrupted Xenomorph Hive" +#define NAME_FACTION_XENOMORPH_ALPHA "Alpha Xenomorph Hive" +#define NAME_FACTION_XENOMORPH_BRAVO "Bravo Xenomorph Hive" +#define NAME_FACTION_XENOMORPH_CHARLIE "Charlie Xenomorph Hive" +#define NAME_FACTION_XENOMORPH_DELTA "Delta Xenomorph Hive" +#define NAME_FACTION_XENOMORPH_FERAL "Feral Xenomorph Hive" +#define NAME_FACTION_XENOMORPH_FORSAKEN "Forsaken Xenomorph Hive" +#define NAME_FACTION_XENOMORPH_TAMED "Tamed Xenomorph Hive" +#define NAME_FACTION_XENOMORPH_MUTATED "Mutated Xenomorph Hive" +#define NAME_FACTION_XENOMORPH_YAUTJA "Yautja Xenomorph Hive" +#define NAME_FACTION_XENOMORPH_RENEGADE "Renegade Xenomorph Hive" + +#define NAME_FACTION_LIST_MARINE list(NAME_FACTION_USCM, NAME_FACTION_MARINE, NAME_FACTION_MARSOC) +#define NAME_FACTION_LIST_WY list(NAME_FACTION_WY, NAME_FACTION_PMC, NAME_FACTION_WY_DEATHSQUAD) +#define NAME_FACTION_LIST_HUMANOID list(NAME_FACTION_NEUTRAL, NAME_FACTION_CLF, NAME_FACTION_UPP, NAME_FACTION_FREELANCER, NAME_FACTION_COLONIST, NAME_FACTION_MERCENARY, NAME_FACTION_DUTCH, NAME_FACTION_HEFA, NAME_FACTION_GLADIATOR, NAME_FACTION_PIRATE, NAME_FACTION_PIZZA, NAME_FACTION_SOUTO, NAME_FACTION_ZOMBIE, NAME_FACTION_YAUTJA) + NAME_FACTION_LIST_MARINE + NAME_FACTION_LIST_WY +#define NAME_FACTION_LIST_XENOMORPH list(NAME_FACTION_XENOMORPH_NORMAL, NAME_FACTION_XENOMORPH_CORRUPTED, NAME_FACTION_XENOMORPH_ALPHA, NAME_FACTION_XENOMORPH_BRAVO, NAME_FACTION_XENOMORPH_CHARLIE, NAME_FACTION_XENOMORPH_DELTA, NAME_FACTION_XENOMORPH_FERAL, NAME_FACTION_XENOMORPH_FORSAKEN, NAME_FACTION_XENOMORPH_TAMED, NAME_FACTION_XENOMORPH_MUTATED, NAME_FACTION_XENOMORPH_YAUTJA, NAME_FACTION_XENOMORPH_YAUTJA) +#define NAME_FACTION_LIST_ALL NAME_FACTION_LIST_HUMANOID + NAME_FACTION_LIST_XENOMORPH + +//ANNOUNCES +#define COMMAND_ANNOUNCE "Command Announcement" +#define UPP_COMMAND_ANNOUNCE "UPP Command Announcement" +#define CLF_COMMAND_ANNOUNCE "CLF Command Announcement" +#define WY_COMMAND_ANNOUNCE "WY Command Announcement" +#define QUEEN_ANNOUNCE "The words of the Queen reverberate in your head..." +#define QUEEN_MOTHER_ANNOUNCE "Queen Mother Psychic Directive" +#define XENO_GENERAL_ANNOUNCE "You sense something unusual..." +#define YAUTJA_ANNOUNCE "You receive a message from your ship AI..." +#define HIGHER_FORCE_ANNOUNCE SPAN_ANNOUNCEMENT_HEADER_BLUE("Unknown Higher Force") + +//TASKS +#define FACTION_TASKS_DOMINATE "Dominate" +#define FACTION_TASKS_DESTROY "Destroy" +#define FACTION_TASKS_SECTOR_OCCUPY "Occupy Sector" +#define FACTION_TASKS_SECTOR_PROTECT "Protect Sector" +#define FACTION_TASKS_SECTOR_HOLD "Hold Sector" +#define FACTION_TASKS_SECTOR_CONTROL "Sector Control" +#define FACTION_TASKS_KILL "Kill" +#define FACTION_TASKS_PROTECT "Protect" +#define FACTION_TASKS_HOLD_TIME "Hold Time" +#define FACTION_TASKS_LIST_ALL list(FACTION_TASKS_DOMINATE, FACTION_TASKS_DESTROY, FACTION_TASKS_SECTOR_OCCUPY, FACTION_TASKS_SECTOR_PROTECT, FACTION_TASKS_SECTOR_CONTROL, FACTION_TASKS_PROTECT, FACTION_TASKS_KILL, FACTION_TASKS_HOLD_TIME) + +// Faction allegiances within a certain faction. +#define FACTION_ALLEGIANCE_USCM_COMMANDER list("Doves", "Hawks", "Magpies", "Unaligned") diff --git a/code/__DEFINES/typecheck/xenos.dm b/code/__DEFINES/typecheck/xenos.dm index 34b70ac92f45..58c518f50bd3 100644 --- a/code/__DEFINES/typecheck/xenos.dm +++ b/code/__DEFINES/typecheck/xenos.dm @@ -31,24 +31,21 @@ if(!istype(attempt_harm_mob)) return FALSE - if(!hive) - hive = GLOB.hive_datum[hivenumber] - - if(!hive) + if(!faction) return FALSE - if(hivenumber == XENO_HIVE_RENEGADE) - var/datum/hive_status/corrupted/renegade/renegade_hive = hive - return renegade_hive.iff_protection_check(src, attempt_harm_mob) + if(faction.faction_flags == HARD_IFF_LOCK) + return faction.iff_protection_check(src, attempt_harm_mob) - return hive.is_ally(attempt_harm_mob) + return attempt_harm_mob.ally(faction) // need this to set the data for walls/eggs/huggers when they are initialized -/proc/set_hive_data(atom/focused_atom, hivenumber) - var/datum/hive_status/hive = GLOB.hive_datum[hivenumber] - if (hive.color) - focused_atom.color = hive.color - focused_atom.name = "[lowertext(hive.prefix)][focused_atom.name]" +/proc/set_hive_data(atom/focused_atom, datum/faction/faction) + if(!faction) + return + if(faction.color) + focused_atom.color = faction.color + focused_atom.name = "[lowertext(faction.prefix)][focused_atom.name]" /proc/get_xeno_stun_duration(mob/stun_mob, duration) if(iscarbonsizexeno(stun_mob)) diff --git a/code/_globalvars/global_lists.dm b/code/_globalvars/global_lists.dm index c6957eefd22c..2869ab26707d 100644 --- a/code/_globalvars/global_lists.dm +++ b/code/_globalvars/global_lists.dm @@ -200,31 +200,12 @@ GLOBAL_LIST_INIT(language_keys, setup_language_keys()) //table of say codes for GLOBAL_REFERENCE_LIST_INDEXED(origins, /datum/origin, name) GLOBAL_LIST_INIT(player_origins, USCM_ORIGINS) -//Xeno hives -GLOBAL_LIST_INIT_TYPED(hive_datum, /datum/hive_status, list( - XENO_HIVE_NORMAL = new /datum/hive_status(), - XENO_HIVE_CORRUPTED = new /datum/hive_status/corrupted(), - XENO_HIVE_ALPHA = new /datum/hive_status/alpha(), - XENO_HIVE_BRAVO = new /datum/hive_status/bravo(), - XENO_HIVE_CHARLIE = new /datum/hive_status/charlie(), - XENO_HIVE_DELTA = new /datum/hive_status/delta(), - XENO_HIVE_FERAL = new /datum/hive_status/feral(), - XENO_HIVE_TAMED = new /datum/hive_status/corrupted/tamed(), - XENO_HIVE_MUTATED = new /datum/hive_status/mutated(), - XENO_HIVE_FORSAKEN = new /datum/hive_status/forsaken(), - XENO_HIVE_YAUTJA = new /datum/hive_status/yautja(), - XENO_HIVE_RENEGADE = new /datum/hive_status/corrupted/renegade(), - XENO_HIVE_TUTORIAL = new /datum/hive_status/tutorial() -)) - GLOBAL_LIST_INIT(xeno_evolve_times, setup_xeno_evolve_times()) /proc/setup_xeno_evolve_times() for(var/datum/caste_datum/caste as anything in subtypesof(/datum/caste_datum)) LAZYADDASSOCLIST(., num2text(initial(caste.minimum_evolve_time)), caste) -GLOBAL_LIST_INIT(custom_event_info_list, setup_custom_event_info()) - // Posters GLOBAL_LIST_INIT(poster_designs, subtypesof(/datum/poster)) @@ -448,26 +429,6 @@ GLOBAL_LIST_INIT(hj_emotes, setup_hazard_joe_emotes()) mobtypes["[T]"] = typecacheof(T.target_mobtypes) return mobtypes -/proc/setup_custom_event_info() - //faction event messages - var/list/custom_event_info_list = list() - var/datum/custom_event_info/CEI = new /datum/custom_event_info - CEI.faction = "Global" //the old public one for whole server to see - custom_event_info_list[CEI.faction] = CEI - for(var/T in FACTION_LIST_HUMANOID) - CEI = new /datum/custom_event_info - CEI.faction = T - custom_event_info_list[T] = CEI - - var/datum/hive_status/hive - for(var/hivenumber in GLOB.hive_datum) - hive = GLOB.hive_datum[hivenumber] - CEI = new /datum/custom_event_info - CEI.faction = hive.internal_faction - custom_event_info_list[hive.name] = CEI - - return custom_event_info_list - /proc/setup_taskbar_icons() var/list/png_list = flist("icons/taskbar") for(var/png in png_list) diff --git a/code/controllers/subsystem/hijack.dm b/code/controllers/subsystem/hijack.dm index 6a2f63023ca2..860af12601b1 100644 --- a/code/controllers/subsystem/hijack.dm +++ b/code/controllers/subsystem/hijack.dm @@ -200,21 +200,20 @@ SUBSYSTEM_DEF(hijack) if(marine_warning_areas) marine_warning_areas = copytext(marine_warning_areas, 1, -2) - var/datum/hive_status/hive - for(var/hivenumber in GLOB.hive_datum) - hive = GLOB.hive_datum[hivenumber] - if(!length(hive.totalXenos)) + for(var/faction_to_get in FACTION_LIST_XENOMORPH) + var/datum/faction/faction = GLOB.faction_datum[faction_to_get] + if(!length(faction.totalMobs)) continue switch(announce) if(1) - xeno_announcement(SPAN_XENOANNOUNCE("The talls are a quarter of the way towards their goals. Disable the following areas: [xeno_warning_areas]"), hive.hivenumber, XENO_HIJACK_ANNOUNCE) + xeno_announcement(SPAN_XENOANNOUNCE("The talls are a quarter of the way towards their goals. Disable the following areas: [xeno_warning_areas]"), faction, XENO_HIJACK_ANNOUNCE) if(2) - xeno_announcement(SPAN_XENOANNOUNCE("The talls are half way towards their goals. Disable the following areas: [xeno_warning_areas]"), hive.hivenumber, XENO_HIJACK_ANNOUNCE) + xeno_announcement(SPAN_XENOANNOUNCE("The talls are half way towards their goals. Disable the following areas: [xeno_warning_areas]"), faction, XENO_HIJACK_ANNOUNCE) if(3) - xeno_announcement(SPAN_XENOANNOUNCE("The talls are three quarters of the way towards their goals. Disable the following areas: [xeno_warning_areas]"), hive.hivenumber, XENO_HIJACK_ANNOUNCE) + xeno_announcement(SPAN_XENOANNOUNCE("The talls are three quarters of the way towards their goals. Disable the following areas: [xeno_warning_areas]"), faction, XENO_HIJACK_ANNOUNCE) if(4) - xeno_announcement(SPAN_XENOANNOUNCE("The talls have completed their goals!"), hive.hivenumber, XENO_HIJACK_ANNOUNCE) + xeno_announcement(SPAN_XENOANNOUNCE("The talls have completed their goals!"), faction, XENO_HIJACK_ANNOUNCE) switch(announce) if(1) @@ -306,13 +305,12 @@ SUBSYSTEM_DEF(hijack) if(!generator_ever_overloaded) generator_ever_overloaded = TRUE - var/datum/hive_status/hive - for(var/hivenumber in GLOB.hive_datum) - hive = GLOB.hive_datum[hivenumber] + for(var/faction_to_get in FACTION_LIST_XENOMORPH) + var/datum/faction/faction = GLOB.faction_datum[faction_to_get] if(!length(hive.totalXenos)) continue - xeno_announcement(SPAN_XENOANNOUNCE("The talls may be attempting to take their ship down with them in Engineering, stop them!"), hive.hivenumber, XENO_HIJACK_ANNOUNCE) + xeno_announcement(SPAN_XENOANNOUNCE("The talls may be attempting to take their ship down with them in Engineering, stop them!"), faction, XENO_HIJACK_ANNOUNCE) adjust_generator_overload_count(new_overloading ? 1 : -1) diff --git a/code/datums/event_info_text.dm b/code/datums/event_info_text.dm index 21469ed379cd..9b433f6e1893 100644 --- a/code/datums/event_info_text.dm +++ b/code/datums/event_info_text.dm @@ -1,18 +1,13 @@ /datum/custom_event_info - var/faction = "default" //here category/faction/hive name stored - var/msg = "" //here is the message itself - + var/name = "default" + var/faction_name = "default" + var/datum/faction/faction = null + var/msg = "" //this shows event info to player. can pass clients and mobs -/datum/custom_event_info/proc/show_player_event_info(user) - - if(!istype(user, /client)) - if(ismob(user)) - var/mob/M = user - if(!M.client) - return - else - return +/datum/custom_event_info/proc/show_player_event_info(client/user) + if(!istype(user)) + return if(msg == "") return @@ -26,7 +21,6 @@ //this shows changed event info to everyone in the category /datum/custom_event_info/proc/handle_event_info_update() - if(!msg) return @@ -37,26 +31,16 @@ to_world(dat) return - else if(faction in FACTION_LIST_HUMANOID) - for(var/mob/M in GLOB.human_mob_list) - if(M && M.faction == faction) - show_player_event_info(M) + else if(faction_name) + for(var/mob/M in faction.totalMobs) + show_player_event_info(M.client) return - else - var/datum/hive_status/hive - for(var/hivenumber in GLOB.hive_datum) - hive = GLOB.hive_datum[hivenumber] - if(hive.name == faction) - for(var/mob/M in hive.totalXenos) - show_player_event_info(M) - return - message_admins("ERROR, ([faction ? faction : "name lost"]) faction is not found for event info.") return -/mob/proc/check_event_info(category = "Global") +/proc/check_event_info(category = "Global", client/user) if(GLOB.custom_event_info_list[category]) var/datum/custom_event_info/CEI = GLOB.custom_event_info_list[category] if(CEI.msg) - CEI.show_player_event_info(src) + CEI.show_player_event_info(user) diff --git a/code/datums/factions/__misc/autobalance_payload.dm b/code/datums/factions/__misc/autobalance_payload.dm new file mode 100644 index 000000000000..b9350cdae6c2 --- /dev/null +++ b/code/datums/factions/__misc/autobalance_payload.dm @@ -0,0 +1,320 @@ +/datum/autobalance_row_faction_info + var/datum/faction/faction + var/esteminated_power = 0 + var/weight = 0 + var/average_fires = 0 + var/list/round_start_pop = list(0, 0, 0) + var/list/average_pop = list(0, 0, 0) + var/list/last_pop = list(0, 0, 0) + +/datum/autobalance_row_faction_info/New(datum/faction/faction_to_set) + ..() + faction = faction_to_set + +/datum/autobalance_row_faction_info/proc/esteminate_faction_info() + var/new_esteminated_power = 0 + var/new_weight = 0 + average_fires++ + last_pop = list(0, 0, 0) + for(var/potantial_row in SSautobalancer.balance_rows) + var/datum/autobalance_row_info/balance_row = SSautobalancer.balance_rows[potantial_row] + if(balance_row.faction_to_set() == faction && balance_row.player_entity.player.owning_client?.mob) + var/role_coeff = faction.get_role_coeff(balance_row.player_entity.player.owning_client.mob.job) + new_esteminated_power += balance_row.get_player_rating() * role_coeff + new_weight += role_coeff + average_pop[balance_row.active]++ + last_pop[balance_row.active]++ + +// for(var/i = 1; i <= length(round_start_pop); i++) +// new_esteminated_power += (last_pop[i] - average_pop[i] / average_fires - round_start_pop[i]) * 100 + + esteminated_power = new_esteminated_power + weight = new_weight + return esteminated_power + +/datum/autobalance_row_faction_info/proc/round_start() + for(var/potantial_row in SSautobalancer.balance_rows) + var/datum/autobalance_row_info/balance_row = SSautobalancer.balance_rows[potantial_row] + if(balance_row.faction_to_set() == faction) + round_start_pop[balance_row.active]++ + +/datum/autobalance_formula_row + var/statistic_type = BALANCE_FORMULA_MISC + +/datum/autobalance_formula_row/proc/calculate(mob/calculationg_mob, datum/player_entity/entity) + var/datum/statistic_groups/group = entity.statistics[calculationg_mob.faction.faction_name] + if(group) + var/list/stats = list() + for(var/group_subtype in group.statistic_info) + var/datum/player_statistic/player_statistic = group.statistic_info[group_subtype] + stats += player_statistic.total + + return formula_calculate(calculationg_mob, entity, stats) + +/datum/autobalance_formula_row/proc/formula_calculate(mob/calculationg_mob, datum/player_entity/entity, list/stats = list()) + var/final_calculations = 0 + var/total_encounters = 0 + var/total_value = 0 + for(var/potential_stat in stats) + if(potential_stat in STATISTIC_MISC_ALL) + total_encounters++ + total_value += stats[potential_stat] + if(total_value) + final_calculations += total_value / total_encounters + return final_calculations + +/datum/autobalance_formula_row/commanding + statistic_type = BALANCE_FORMULA_COMMANDING + +/datum/autobalance_formula_row/commanding/formula_calculate(mob/calculationg_mob, datum/player_entity/entity, list/stats = list()) + var/final_calculations = 0 + var/total_value = 0 + for(var/potential_stat in stats) + if(potential_stat in STATISTIC_ALL) + total_value += stats[potential_stat] + if(total_value) + final_calculations += total_value / length(STATISTIC_ALL) + return final_calculations + +/datum/autobalance_formula_row/field + statistic_type = BALANCE_FORMULA_FIELD + +/datum/autobalance_formula_row/field/formula_calculate(mob/calculationg_mob, datum/player_entity/entity, list/stats = list()) + var/final_calculations = 0 + var/list/kda = new(2) + var/list/shots = new(2) + var/damage = 0 + for(var/potential_stat in stats) + switch(potential_stat) + if(STATISTICS_KILL) + kda[1] += stats[potential_stat] + if(STATISTICS_KILL_FF) + kda[1] -= stats[potential_stat] + if(STATISTICS_DEATH) + kda[2] += stats[potential_stat] + if(STATISTICS_DEATH_FF) + kda[2] -= stats[potential_stat] + if(STATISTICS_SHOT) + shots[1] += stats[potential_stat] + if(STATISTICS_SHOT_HIT) + shots[2] += stats[potential_stat] + if(STATISTICS_FF_SHOT_HIT) + shots[2] -= stats[potential_stat] + if(STATISTICS_DAMAGE) + damage += stats[potential_stat] + if(STATISTICS_FF_DAMAGE) + damage -= stats[potential_stat] + + if(kda[1] && kda[2]) + final_calculations += kda[1] / kda[2] * 100 + if(shots[1] && shots[2]) + final_calculations += shots[2] / shots[1] * 100 + if(damage && kda[1]) + final_calculations += damage / kda[1] + return final_calculations + + +/datum/autobalance_formula_row/support + statistic_type = BALANCE_FORMULA_SUPPORT + +/datum/autobalance_formula_row/support/formula_calculate(mob/calculationg_mob, datum/player_entity/entity, list/stats = list()) + var/final_calculations = 0 + var/stats_assists = 0 + var/rounds = 0 + for(var/potential_stat in stats) + switch(potential_stat) + if(STATISTIC_ASSIST_ALL) + stats_assists += stats[potential_stat] + if(STATISTICS_ROUNDS_PLAYED) + rounds += stats[potential_stat] + + if(stats_assists) + final_calculations += stats_assists / rounds + return final_calculations + +/datum/autobalance_formula_row/support/medic + statistic_type = BALANCE_FORMULA_MEDIC + +/datum/autobalance_formula_row/support/medic/formula_calculate(mob/calculationg_mob, datum/player_entity/entity, list/stats = list()) + var/final_calculations = 0 + var/healed = 0 + var/revives = 0 + var/rounds = 0 + for(var/potential_stat in stats) + switch(potential_stat) + if(STATISTICS_HEALED_DAMAGE) + healed += stats[potential_stat] + if(STATISTICS_REVIVE) + revives += stats[potential_stat] + if(STATISTICS_ROUNDS_PLAYED) + rounds += stats[potential_stat] + + if(healed) + final_calculations += healed / rounds + if(revives) + final_calculations += revives / rounds * 100 + return final_calculations + +/datum/autobalance_formula_row/support/operations + statistic_type = BALANCE_FORMULA_OPERATIONS + +/datum/autobalance_formula_row/support/operations/formula_calculate(mob/calculationg_mob, datum/player_entity/entity, list/stats = list()) + var/final_calculations = 0 + var/surgeries = 0 + var/rounds = 0 + for(var/potential_stat in stats) + switch(potential_stat) + if(STATISTIC_SURGERY_ALL) + surgeries += stats[potential_stat] + if(STATISTICS_ROUNDS_PLAYED) + rounds += stats[potential_stat] + + if(surgeries) + final_calculations += surgeries / rounds + return final_calculations + +/datum/autobalance_formula_row/support/engineer + statistic_type = BALANCE_FORMULA_ENGINEER + +/datum/autobalance_formula_row/support/engineer/formula_calculate(mob/calculationg_mob, datum/player_entity/entity, list/stats = list()) + var/final_calculations = 0 + var/engi_stats = 0 + var/rounds = 0 + for(var/potential_stat in stats) + if(potential_stat in STATISTIC_ENGINEERING_ALL) + engi_stats += stats[potential_stat] + else if(potential_stat == STATISTICS_ROUNDS_PLAYED) + rounds += stats[potential_stat] + + if(engi_stats) + final_calculations += engi_stats / rounds * 100 + return final_calculations + +/datum/autobalance_formula_row/xeno_fighter + statistic_type = BALANCE_FORMULA_XENO_FIGHTER + +/datum/autobalance_formula_row/xeno_fighter/formula_calculate(mob/calculationg_mob, datum/player_entity/entity, list/stats = list()) + var/final_calculations = 0 + var/list/kda = new(2) + var/slashes = 0 + var/damage = 0 + for(var/potential_stat in stats) + switch(potential_stat) + if(STATISTICS_KILL) + kda[1] += stats[potential_stat] + if(STATISTICS_KILL_FF) + kda[1] -= stats[potential_stat] + if(STATISTICS_DEATH) + kda[2] += stats[potential_stat] + if(STATISTICS_DEATH_FF) + kda[2] -= stats[potential_stat] + if(STATISTICS_SLASH) + slashes += stats[potential_stat] + if(STATISTICS_DAMAGE) + damage += stats[potential_stat] + if(STATISTICS_FF_DAMAGE) + damage -= stats[potential_stat] + + if(kda[1] && kda[2]) + final_calculations += kda[1] / kda[2] * 100 + if(slashes && kda[1]) + final_calculations += slashes / kda[1] + if(damage && kda[1]) + final_calculations += damage / kda[1] + return final_calculations + +/datum/autobalance_formula_row/xeno_healer + statistic_type = BALANCE_FORMULA_XENO_HEALER + +/datum/autobalance_formula_row/xeno_healer/formula_calculate(mob/calculationg_mob, datum/player_entity/entity, list/stats = list()) + var/final_calculations = 0 + var/healed = 0 + var/rounds = 0 + for(var/potential_stat in stats) + switch(potential_stat) + if(STATISTICS_HEALED_DAMAGE) + healed += stats[potential_stat] + if(STATISTICS_ROUNDS_PLAYED) + rounds += stats[potential_stat] + + if(healed) + final_calculations += healed / rounds + return final_calculations + +/datum/autobalance_formula_row/xeno_builder + statistic_type = BALANCE_FORMULA_XENO_BUILDER + +/datum/autobalance_formula_row/xeno_builder/formula_calculate(mob/calculationg_mob, datum/player_entity/entity, list/stats = list()) + var/final_calculations = 0 + var/build = 0 + var/rounds = 0 + for(var/potential_stat in stats) + switch(potential_stat) + if(STATISTIC_XENO_STRUCTURES_BUILD) + build += stats[potential_stat] + if(STATISTICS_ROUNDS_PLAYED) + rounds += stats[potential_stat] + + if(build) + final_calculations += build / rounds + return final_calculations + +/datum/autobalance_formula_row/xeno_abiliter + statistic_type = BALANCE_FORMULA_XENO_ABILITER + +/datum/autobalance_formula_row/xeno_abiliter/formula_calculate(mob/calculationg_mob, datum/player_entity/entity, list/stats = list()) + var/final_calculations = 0 + var/abilities = 0 + var/rounds = 0 + for(var/potential_stat in stats) + switch(potential_stat) + if(STATISTICS_ABILITES) + abilities += stats[potential_stat] + if(STATISTICS_ROUNDS_PLAYED) + rounds += stats[potential_stat] + + if(abilities) + final_calculations += abilities / rounds + return final_calculations + +/datum/autobalance_row_info + var/rating = 0 + var/active = 3 + var/datum/player_entity/player_entity = null + var/datum/entity/player/player = null + +/datum/autobalance_row_info/New(datum/player_entity/entity_to_set) + ..() + player_entity = entity_to_set + +/datum/autobalance_row_info/proc/get_player_rating() + rating = 0 + var/mob/living = player_entity.player.owning_client?.mob + if(istype(living) && istype(living.job, /datum/job)) + var/datum/job/player_job = GET_MAPPED_ROLE(living.job) + for(var/balance_formula in player_job.balance_formulas + living.balance_formulas) + var/datum/autobalance_formula_row/formula = GLOB.balance_formulas[balance_formula] + var/additional_rating = formula.calculate(living, player_entity) + if(additional_rating) + rating += additional_rating / length(player_job.balance_formulas + living.balance_formulas) + return rating + +/datum/autobalance_row_info/proc/faction_to_set() + var/mob/living = player_entity.player.owning_client?.mob + if(living) + return living.faction + return FALSE + +/datum/autobalance_row_info/proc/status_change(action) + switch(action) + if("login") + active = 3 + if("logout") + active = 2 + if("death") + active = 1 + if("revive") + if(player_entity.player.owning_client?.mob?.client) + active = 3 + else + active = 2 diff --git a/code/datums/factions/__misc/faction_tasks.dm b/code/datums/factions/__misc/faction_tasks.dm new file mode 100644 index 000000000000..774dcdc7b5fe --- /dev/null +++ b/code/datums/factions/__misc/faction_tasks.dm @@ -0,0 +1,472 @@ +/datum/faction_task + var/name = "INSERT" + var/desc = "INSERT" + var/list/announce_desc = list("complete" = "Good job, sector taken, ", "failed" = "Sector control failed") + var/state = OBJECTIVE_INACTIVE + + var/score = 0 + + var/game_ender = FALSE + var/rand_total_time = FALSE + var/total_time = 0 + COOLDOWN_DECLARE(remaining_time) + + var/task_type + var/task_color + var/datum/faction/faction_owner + +/datum/faction_task/New(datum/faction/faction_to_set) + . = ..() + + if(rand_total_time) + total_time += rand(-total_time, total_time) / 2 + + faction_owner = faction_to_set + SSfactions.add_task(src) + activate() + +/datum/faction_task/Destroy() + SSfactions.stop_processing_task(src) + SSfactions.remove_task(src) + return ..() + +/datum/faction_task/proc/activate() + if(total_time) + COOLDOWN_START(src, total_time, total_time) + SSfactions.start_processing_task(src) + +/datum/faction_task/proc/deactivate() + SSfactions.stop_processing_task(src) + +/datum/faction_task/proc/check_completion() + return + +/datum/faction_task/proc/complete(end_state) + state = end_state + + if(state & OBJECTIVE_COMPLETE) + faction_owner.faction_victory_points += score + announce_faction() + +/datum/faction_task/proc/announce_faction() + faction_announcement(state & OBJECTIVE_COMPLETE ? announce_desc["complete"] : announce_desc["failed"], name, null, faction_owner) + +/datum/faction_task/proc/get_completion_status() + if(state & OBJECTIVE_IN_PROGRESS) + return "In Progress!" + else if(state & OBJECTIVE_FAILED) + return "Failed!" + else if(state & OBJECTIVE_COMPLETE) + return "Succeeded!" + return "Awaiting" + +/datum/faction_task/proc/get_readable_progress() + var/dat = "[name]: " + return dat + get_completion_status() + "
" + +//////////////////////////////////////////////////////////////////////////////////////// +/obj/structure/prop/sector_center + name = FACTION_TASKS_SECTOR_CONTROL + desc = "To seize this object is to take victory in one hand" + icon = 'icons/obj/structures/sector_control.dmi' + icon_state = "tower" + breakable = FALSE + indestructible = TRUE + unacidable = TRUE + unslashable = TRUE + density = TRUE + + var/capture_progress = 0 + var/req_capture_progress = 100 + var/zone_range = 8 + var/home_sector = FALSE + + var/sector_id = "0" + var/list/sector_connections = list() + + faction_to_get = null + + var/datum/faction_task/sector_control/owner + + var/list/connected_task = list() + var/list/bordered_sectors = list() + var/list/linked_turfs = list() + + var/obj/effect/decal/fog //fog + var/datum/shape/rectangle/range_bounds + +/obj/structure/prop/sector_center/Initialize() + . = ..() + name = "Sector [pick(operation_prefixes)]-[pick(operation_postfixes)]" + owner = new(faction, src) + range_bounds = RECT(loc.x, loc.y, zone_range * 2, zone_range * 2) + update_icon() + if(home_sector) + capture_progress = req_capture_progress + + fog = new(owner) + + for(var/turf/turf in range(round(zone_range*COVERAGE_MULT), loc)) + LAZYADD(turf.linked_sectors, src) + linked_turfs += turf + + START_PROCESSING(SSslowobj, src) + +/obj/structure/prop/sector_center/Destroy() + . = ..() + for(var/turf/turf as anything in linked_turfs) + LAZYREMOVE(turf.linked_sectors, src) + range_bounds = null + STOP_PROCESSING(SSslowobj, src) + +/obj/structure/prop/sector_center/get_examine_text(mob/user) + . = ..() + . += "Capture progress [capture_progress/req_capture_progress*100]% ([capture_progress]/[req_capture_progress])," + if(faction) + . += " by faction [faction]," + . += "
Zone radius [zone_range] m.
" + +/obj/structure/prop/sector_center/process() + var/turf/turf = get_turf(src) + if(!istype(turf)) + return + + if(!range_bounds) + range_bounds = RECT(turf.x, turf.y, zone_range * 2, zone_range * 2) + + var/list/candidates = SSquadtree.players_in_range(range_bounds, turf.z, QTREE_EXCLUDE_OBSERVER | QTREE_SCAN_MOBS) + + var/total_ammount = 0 + var/list/sorted_in_range = list() + for(var/atom in candidates) + var/mob/living/carbon/carbon = atom + if(carbon.faction) + if(!length(sorted_in_range[carbon.faction.faction_name])) + sorted_in_range[carbon.faction.faction_name] = list() + total_ammount++ + sorted_in_range[carbon.faction.faction_name] += carbon + + var/datum/faction/potential_faction + var/potential_number = 0 + var/faction_mobs_number = 0 + for(var/faction_to_get in sorted_in_range) + faction_mobs_number = length(sorted_in_range[faction_to_get]) + if(potential_number < faction_mobs_number) + potential_faction = GLOB.faction_datum[faction_to_get] + potential_number = faction_mobs_number + + var/have_task = FALSE + if(potential_faction && length(get_faction_tasks(potential_faction))) + have_task = TRUE + + var/overhelming = potential_number * 2 - total_ammount + var/overhelming_to_add = potential_number * 2 - total_ammount + if(!have_task) + overhelming_to_add = 1 + + if(!length(candidates) && capture_progress != req_capture_progress) + capture_progress-- + + else if(capture_progress != req_capture_progress) + if(faction != potential_faction) + if(overhelming) + if(capture_progress > 1) + capture_progress -= overhelming_to_add + else if(capture_progress < 1 && !faction) + get_sector(potential_faction) + capture_progress = 1 + else + capture_progress-- + else + if(overhelming) + capture_progress += overhelming_to_add + else + capture_progress-- + else + if(faction != potential_faction) + if(overhelming) + capture_progress -= overhelming_to_add + + if(capture_progress == req_capture_progress && faction) + faction.faction_victory_points += home_sector ? 5 : 1 + capture_progress = Clamp(capture_progress, 0, req_capture_progress) + if(!capture_progress && faction) + get_sector(null) + +/obj/structure/prop/sector_center/proc/get_sector(datum/faction/check_faction) + if(!check_faction) + faction_announcement("[name] lost!", FACTION_TASKS_SECTOR_CONTROL, null, faction) + faction = null +// fog.faction = null + return + var/announce_text = "[name] new controlled by [check_faction]" + if(home_sector) + var/datum/faction/initial_faction = GLOB.faction_datum[faction_to_get] + if(initial_faction == faction) + faction_announcement("[name] new controlled by [check_faction], home sector occupied, now for us that big problem!", FACTION_TASKS_SECTOR_CONTROL, null, faction) + faction_announcement("[name] new controlled by [check_faction], home sector returned back, now your enemy don't can do anything!", FACTION_TASKS_SECTOR_CONTROL, null, initial_faction) + initial_faction.latejoin_enabled = FALSE + else if(initial_faction == check_faction) + faction_announcement("[name] new controlled by [check_faction], home sector occupied, enemy now can comeback!", FACTION_TASKS_SECTOR_CONTROL, null, faction) + faction_announcement("[name] new controlled by [check_faction], home sector returned back, rise and destory our enemies!", FACTION_TASKS_SECTOR_CONTROL, null, initial_faction) + initial_faction.latejoin_enabled = TRUE + else + faction_announcement(announce_text, FACTION_TASKS_SECTOR_CONTROL, null, faction) + faction_announcement(announce_text, FACTION_TASKS_SECTOR_CONTROL, null, check_faction) + else + faction_announcement(announce_text, FACTION_TASKS_SECTOR_CONTROL, null, faction) + faction_announcement(announce_text, FACTION_TASKS_SECTOR_CONTROL, null, check_faction) + faction = check_faction +// fog.faction = faction + +/obj/structure/prop/sector_center/proc/captured(datum/faction/check_faction) + if(faction == check_faction && capture_progress == req_capture_progress) + return TRUE + return FALSE + +/obj/structure/prop/sector_center/proc/get_faction_tasks(datum/faction/check_faction) + var/list/tasks_list = list() + var/list/potential_tasks = connected_task[check_faction.faction_name] + for(var/datum/faction_task/task in potential_tasks) + if(task.faction_owner == check_faction) + tasks_list += task + return tasks_list + +/obj/structure/prop/sector_center/base + req_capture_progress = 600 + zone_range = 16 + home_sector = TRUE + +//////////////////////////////////////////////////////////////////////////////////////// +/datum/faction_task/sector_control + name = FACTION_TASKS_SECTOR_CONTROL + desc = "You need to control ###SECTOR### sector as long as can" + announce_desc = list("complete" = "Good job, sector taken", "failed" = "Sector control failed") + + task_type = FACTION_TASKS_SECTOR_CONTROL + task_color = "#1616a0" + + var/obj/structure/prop/sector_center/sector_center + var/datum/faction_task/sector_control/grooped_task + +/datum/faction_task/sector_control/New(datum/faction/faction_to_set, obj/structure/prop/sector_center/sector) + . = ..() + sector_center = sector + desc = replacetext(desc, "###SECTOR###", sector.name) + sector_center.connected_task += src + +/datum/faction_task/sector_control/process() + sector_center.process() + +/datum/faction_task/sector_control/announce_faction() + faction_announcement("[sector_center] [state & OBJECTIVE_COMPLETE ? announce_desc["complete"] : announce_desc["failed"]]", name, null, faction_owner) + +//////////////////////////////////////////////////////////////////////////////////////// +/datum/faction_task/sector_control/protect + name = FACTION_TASKS_SECTOR_PROTECT + desc = "Defend ###SECTOR### sector" + announce_desc = list("complete" = "Good job, sector taken", "failed" = "Sector control failed") + + score = 1500 + + total_time = 0 + +/datum/faction_task/sector_control/protect/process() + return + +/datum/faction_task/sector_control/protect/check_completion() + if(!sector_center.captured(faction_owner)) + complete(OBJECTIVE_FAILED) + else if(COOLDOWN_FINISHED(src, remaining_time)) + complete(OBJECTIVE_COMPLETE) + +//////////////////////////////////////////////////////////////////////////////////////// +/datum/faction_task/sector_control/occupy + name = FACTION_TASKS_SECTOR_OCCUPY + desc = "Capture ###SECTOR### sector" + announce_desc = list("complete" = "Good job, sector taken", "failed" = "Sector control failed") + + score = 2250 + + total_time = 15 MINUTES + +/datum/faction_task/sector_control/occupy/New(datum/faction/faction_to_set, obj/structure/prop/sector_center/sector) + . = ..() + if(sector.faction) + grooped_task = new /datum/faction_task/sector_control/protect(sector.faction, sector) + SSfactions.active_tasks += grooped_task + +/datum/faction_task/sector_control/occupy/check_completion() + if(!sector_center.captured(faction_owner) || COOLDOWN_FINISHED(src, remaining_time)) + complete(OBJECTIVE_FAILED) + grooped_task.complete(OBJECTIVE_COMPLETE) + else if(sector_center.captured(faction_owner)) + complete(OBJECTIVE_COMPLETE) + grooped_task.complete(OBJECTIVE_FAILED) + +//////////////////////////////////////////////////////////////////////////////////////// +/datum/faction_task/sector_control/occupy/hold + name = FACTION_TASKS_SECTOR_HOLD + desc = "Dig in and hold ###SECTOR### sector" + announce_desc = list("complete" = "Good job, sector taken", "failed" = "Sector control failed") + + total_time = 30 MINUTES + +/datum/faction_task/sector_control/occupy/hold/process() + if(sector_center.captured(faction_owner)) + score++ + +/datum/faction_task/sector_control/occupy/hold/check_completion() + if(COOLDOWN_FINISHED(src, remaining_time)) + if(score) + complete(OBJECTIVE_COMPLETE) + else + complete(OBJECTIVE_FAILED) + +//////////////////////////////////////////////////////////////////////////////////////// +/datum/faction_task/dominate + name = FACTION_TASKS_DOMINATE + desc = "Destroy all enemy forces and capture all strategic points" + announce_desc = list("complete" = "All enemy forces destroyed!", "failed" = "Enemy overhelmed and destroyed our forces") + + task_type = "Game Ender" + task_color = "#b23131" + +/datum/faction_task/dominate/check_completion() + faction_owner.faction_victory_points -= score + faction_owner.homes_sector_occupation = TRUE + score = 0 + + var/list/faction_stats = list("dead_enemy_factions" = 0, "total_friendly_factions" = 0) + for(var/faction_name in SSticker.mode.factions_pool) + var/datum/faction/faction = GLOB.faction_datum[SSticker.mode.factions_pool[faction_name]] + if(faction_owner.relations_datum.allies[faction.faction_name]) + faction_stats["total_friendly_factions"]++ + + else if(!length(faction.totalMobs)) + faction_stats["dead_enemy_factions"]++ + score += length(faction.totalDeadMobs) + + faction_owner.faction_victory_points += score + + if(faction_stats["dead_enemy_factions"] >= length(SSticker.mode.factions_pool) - length(faction_stats["total_friendly_factions"])) + complete(OBJECTIVE_COMPLETE) + +/datum/faction_task/dominate/complete(end_state) + . = ..() + if(state == OBJECTIVE_COMPLETE) + SSticker.mode.round_finished = SSticker.mode.faction_round_end_state[faction_owner.faction_name] + SSticker.mode.faction_won = faction_owner + +//////////////////////////////////////////////////////////////////////////////////////// +/datum/faction_task/hold + name = FACTION_TASKS_HOLD_TIME + desc = "Hold positions until enemy reserves are depleted" + announce_desc = list("complete" = "You survived, await main army, we already there!", "failed" = "This is cvaudrant lost, you failed your one small job, idiots!") + + rand_total_time = TRUE + total_time = 2 HOURS + + task_type = "Game Ender" + task_color = "#b23131" + +/datum/faction_task/hold/process() + faction_owner.faction_victory_points++ + +/datum/faction_task/hold/check_completion() + if(!length(faction_owner.totalDeadMobs)) + complete(OBJECTIVE_FAILED) + else if(COOLDOWN_FINISHED(src, remaining_time)) + complete(OBJECTIVE_COMPLETE) + +/datum/faction_task/hold/complete(end_state) + . = ..() + if(state == OBJECTIVE_COMPLETE) + SSticker.mode.round_finished = SSticker.mode.faction_round_end_state[faction_owner.faction_name] + SSticker.mode.faction_won = faction_owner + +/datum/faction_task/hold/get_completion_status() + if(state & OBJECTIVE_IN_PROGRESS) + return "Remaining time: [COOLDOWN_TIMELEFT(src, remaining_time)]" + else if(state & OBJECTIVE_FAILED) + return "Failed! Everyone's dead!" + else if(state & OBJECTIVE_COMPLETE) + return "Succeeded! Time's up!" + return "Awaiting" + +//////////////////////////////////////////////////////////////////////////////////////// +/datum/faction_task/destroy + name = FACTION_TASKS_DESTROY + desc = "Destroy a target" + announce_desc = list("complete" = "Good job, sector taken, ", "failed" = "Sector control failed") + + score = 700 + + total_time = 25 MINUTES + + task_type = "Objectives" + task_color = "#1eb641" + + var/datum/faction/destroyed_faction + +/datum/faction_task/destroy/New(datum/faction/faction_to_set, obj/target) + . = ..() + RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(object_destroyed), override = TRUE) + +/datum/faction_task/destroy/proc/object_destroyed(obj/target, datum/faction/faction) + SIGNAL_HANDLER + destroyed_faction = faction + +/datum/faction_task/destroy/check_completion() + if(COOLDOWN_FINISHED(src, remaining_time)) + complete(OBJECTIVE_FAILED) + + else if(destroyed_faction) + if(destroyed_faction != faction_owner) + complete(OBJECTIVE_FAILED) + else + complete(OBJECTIVE_COMPLETE) + +/* +#define FACTION_TASKS_KILL "Kill Task" +#define FACTION_TASKS_PROTECT "Protect Task" +*/ +#define TASK_STATUS_LIST list(OBJECTIVE_COMPLETE = "complete", OBJECTIVE_FAILED = "failed", OBJECTIVE_IN_PROGRESS = "in progress", OBJECTIVE_ACTIVE = "active", OBJECTIVE_INACTIVE = "inactive") +#define TGUI_TASK_COLORS list("#b23131" = "red", "#1eb641" = "green", "#1616a0" = "blue") + +/datum/faction_task_ui + var/datum/faction/faction + +/datum/faction_task_ui/New(datum/faction/faction_to_set) + faction = faction_to_set + +/datum/faction_task_ui/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "FactionTask", "[faction] Tasks") + ui.open() + ui.set_autoupdate(TRUE) + +/datum/faction_task_ui/ui_data(mob/user) + . = list() + var/list/task_payload = list() + for(var/datum/faction_task/task in SSfactions.active_tasks) + if(task.faction_owner != faction) + continue + task_payload += list( + "name" = task.name, + "desc" = task.desc, + "status" = TASK_STATUS_LIST[task.state], + "status_desc" = task.get_completion_status(), + "type" = task.task_type, + "color" = TGUI_TASK_COLORS[task.task_color] + ) + .["tasks"] = task_payload + .["points"] = faction.faction_victory_points + .["req_points"] = 10000 + +/datum/faction_task_ui/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + +/datum/faction_task_ui/ui_status(mob/user, datum/ui_state/state) + return UI_INTERACTIVE diff --git a/code/datums/factions/__misc/helpers.dm b/code/datums/factions/__misc/helpers.dm new file mode 100644 index 000000000000..b36af84e0ba6 --- /dev/null +++ b/code/datums/factions/__misc/helpers.dm @@ -0,0 +1,39 @@ + +GLOBAL_LIST_INIT_TYPED(objectives_reward_list, /datum/objectives_reward, create_objectives_rewards_list()) +GLOBAL_LIST_EMPTY_TYPED(objective_controller, /datum/objectives_datum) +GLOBAL_LIST_INIT(objectives_links, list( + "objective" = list(/obj/item/storage/fancy/vials/random, /obj/item/disk/objective, /obj/item/document_objective/technical_manual, /obj/item/document_objective/paper), + "close" = list(/obj/item/disk/objective, /obj/item/document_objective/technical_manual, /obj/item/document_objective/folder, /obj/item/document_objective/report, /obj/item/document_objective/paper), + "medium" = list(/obj/item/device/mass_spectrometer/adv/objective, /obj/item/device/reagent_scanner/adv/objective, /obj/item/device/healthanalyzer/objective, /obj/item/device/autopsy_scanner/objective, /obj/item/document_objective/paper), + "far" = list(/obj/item/storage/fancy/vials/random, /obj/item/paper/research_notes, /obj/item/disk/objective,/obj/item/document_objective/folder, /obj/item/document_objective/report, /obj/item/document_objective/paper), + "science" = list(/obj/item/storage/fancy/vials/random, /obj/item/paper/research_notes, /obj/item/document_objective/paper) +)) + +GLOBAL_LIST_INIT(task_gen_list, list("sector_control" = list(/datum/faction_task/sector_control/occupy, /datum/faction_task/sector_control/occupy/hold))) +GLOBAL_LIST_INIT(task_gen_list_game_enders, list("game_enders" = list(/datum/faction_task/dominate, /datum/faction_task/hold))) + +GLOBAL_LIST_INIT_TYPED(faction_datum, /datum/faction, setup_faction_list()) + +/proc/setup_faction_list() + . = list() + for(var/faction_to_get in FACTION_LIST_DEFCONED) + var/datum/objectives_datum/objectives_datum = new (faction_to_get) + GLOB.objective_controller[faction_to_get] = objectives_datum + for(var/path in typesof(/datum/faction)) + var/datum/faction/faction = new path + .[faction.faction_name] = faction + faction.relations_datum.generate_relations_helper() + +GLOBAL_LIST_INIT_TYPED(custom_event_info_list, /datum/custom_event_info, setup_custom_event_info()) + +/proc/setup_custom_event_info() + . = list() + var/datum/custom_event_info/CEI = new() + CEI.faction_name = "Global" + .[CEI.faction_name] = CEI + for(var/faction_to_get in FACTION_LIST_ALL) + var/datum/faction/faction = GLOB.faction_datum[faction_to_get] + CEI = new() + CEI.faction_name = faction.name + CEI.faction = faction + .[CEI.faction_name] = CEI diff --git a/code/datums/factions/__misc/relations_actions.dm b/code/datums/factions/__misc/relations_actions.dm new file mode 100644 index 000000000000..5642c221b4fc --- /dev/null +++ b/code/datums/factions/__misc/relations_actions.dm @@ -0,0 +1,75 @@ +/datum/relations_action + var/name = "TELL A DEV" + var/desc = "TELL A DEV" + + var/act_name = "Set name" + var/act_desc = "Set description" + var/act_additional_info = "Set additional info" + + var/accepted = FALSE + var/datum/faction/target_faction + var/confirmed = FALSE + var/datum/faction/initiator_faction + + var/time_delegated + var/time_given = 5 MINUTES + + var/completion_initialy = FALSE + + var/expirable = FALSE + var/start_acting_time + var/expiring + +/datum/relations_action/New(datum/faction/target_faction_to_set, datum/faction/initiator_faction_to_set) + target_faction = target_faction_to_set + initiator_faction = initiator_faction_to_set + time_delegated = world.time + +/datum/relations_action/Destroy() + target_faction = null + initiator_faction = null + . = ..() + +/datum/relations_action/proc/offer() + +/datum/relations_action/proc/check_completion() + +/datum/relations_action/proc/can_accept() + +/datum/relations_action/proc/accept() + +/datum/relations_action/proc/can_confirm() + +/datum/relations_action/proc/confirmation() + +/datum/relations_action/proc/start_acting() + if(expirable) + start_acting_time = world.time + + if(completion_initialy) + completion() + +/datum/relations_action/proc/completion() + +/datum/relations_action/proc/expire() + +////////////// +/datum/relations_action/alliance_request + name = "Пакт для создания союза" + desc = "Две стороны становятся союзниками, тем самым до момента разрыва договора не мешают друг другу" + + completion_initialy = TRUE + +/datum/relations_action/alliance_request/completion() + initiator_faction.relations_datum.allies[target_faction.faction_name] = target_faction + target_faction.relations_datum.allies[initiator_faction.faction_name] = initiator_faction + initiator_faction.relations_datum.gain_opinion(target_faction, 200) + target_faction.relations_datum.gain_opinion(initiator_faction, 200) + +/datum/relations_action/ + + +/* +оскорблеения + +*/ diff --git a/code/datums/factions/__misc/relations_datum.dm b/code/datums/factions/__misc/relations_datum.dm new file mode 100644 index 000000000000..1ab9ddf4ac9b --- /dev/null +++ b/code/datums/factions/__misc/relations_datum.dm @@ -0,0 +1,60 @@ +/datum/faction_relations + var/relations[] = RELATIONS_MAP + var/list/allies = list() + var/list/datum/relations_action/relation_actions = list() + var/datum/faction/faction + var/atom/source + +/datum/faction_relations/New(datum/faction/faction_to_set, atom/referenced_source) + faction = faction_to_set + if(referenced_source) + source = referenced_source + else + source = src + +/datum/faction_relations/proc/generate_relations_helper() + spawn(30 SECONDS) + for(var/i in FACTION_LIST_ALL) + if(i == faction.faction_name) + relations[i] = RELATIONS_SELF + continue + if(i in faction.relations_pregen) + relations[i] = rand(faction.relations_pregen[i][1], faction.relations_pregen[i][2]) + if(RELATIONS_FRIENDLY[2] < faction.relations_pregen[i]) + allies += GLOB.faction_datum[i] + continue + relations[i] = RELATIONS_UNKNOWN + +/datum/faction_relations/proc/can_acting(datum/faction/target_faction) + if(isnull(relations[target_faction.faction_name]) || relations[target_faction.faction_name] < RELATIONS_WAR[1] || relations[target_faction.faction_name] > RELATIONS_MAX) + return FALSE + return TRUE + +/datum/faction_relations/proc/gain_opinion(datum/faction/target_faction, opinion) + relations[target_faction.faction_name] = clamp(relations[target_faction.faction_name] + opinion, RELATIONS_WAR[1], RELATIONS_MAX) + +/datum/faction_relations/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, source, ui) + if(!ui) + ui = new(user, source, "FactionRelations", "[faction] Relations") + ui.open() + ui.set_autoupdate(TRUE) + +/datum/faction_relations/ui_data(mob/user) + . = list() + var/list/relations_mapping = list() + for(var/i in relations) + if(relations[i] == null || relations[i] > 1000) + continue + relations_mapping += list(list("name" = GLOB.faction_datum[i].name, "desc" = GLOB.faction_datum[i].desc, "color" = GLOB.faction_datum[i].ui_color, "value" = relations[i])) + + .["actions"] = source != src ? TRUE : FALSE + + .["faction_color"] = faction.ui_color + .["faction_relations"] = relations_mapping + +/datum/faction_relations/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + +/datum/faction_relations/ui_status(mob/user, datum/ui_state/state) + return UI_INTERACTIVE diff --git a/code/datums/factions/_modules/hive.dm b/code/datums/factions/_modules/hive.dm new file mode 100644 index 000000000000..468b0155fb94 --- /dev/null +++ b/code/datums/factions/_modules/hive.dm @@ -0,0 +1,1438 @@ +/* + * This module handle hive status, please be calm and don't be pathetic, best solution. + * :CLUELES_FACE: +*/ + +/datum/faction_module/hive + /// Oversier faction ref + var/internal_faction + + /// Short Hive ID as string used in stats reporting + var/reporting_id = "normal" + + var/hivenumber = XENO_HIVE_NORMAL + var/mob/living/carbon/xenomorph/queen/living_xeno_queen + var/egg_planting_range = 15 + var/slashing_allowed = XENO_SLASH_ALLOWED //This initial var allows the queen to turn on or off slashing. Slashing off means harm intent does much less damage. + var/construction_allowed = NORMAL_XENO //Who can place construction nodes for special structures + var/destruction_allowed = NORMAL_XENO //Who can destroy special structures + var/unnesting_allowed = TRUE + var/hive_orders = "" //What orders should the hive have + var/color = null + var/ui_color = null // Color for hive status collapsible buttons and xeno count list + var/prefix = "" + var/queen_leader_limit = 2 + var/list/open_xeno_leader_positions = list(1, 2) // Ordered list of xeno leader positions (indexes in xeno_leader_list) that are not occupied + var/list/xeno_leader_list[2] // Ordered list (i.e. index n holds the nth xeno leader) + var/stored_larva = 0 + + ///used by /datum/faction_module/hive/proc/increase_larva_after_burst() to support non-integer increases to larva + var/partial_larva = 0 + /// Assoc list of free slots available to specific castes + var/list/free_slots = list( + /datum/caste_datum/burrower = 1, + /datum/caste_datum/hivelord = 1, + /datum/caste_datum/carrier = 1 + ) + /// Assoc list of slots currently used by specific castes (for calculating free_slot usage) + var/list/used_slots = list() + /// list of living tier2 xenos + var/list/tier_2_xenos = list() + /// list of living tier3 xenos + var/list/tier_3_xenos = list() + /// list of living xenos + var/list/totalXenos = list() + /// list of previously living xenos (hardrefs currently) + var/list/total_dead_xenos = list() + var/xeno_queen_timer + var/isSlotOpen = TRUE //Set true for starting alerts only after the hive has reached its full potential + var/allowed_nest_distance = 15 //How far away do we allow nests from an ovied Queen. Default 15 tiles. + var/obj/effect/alien/resin/special/pylon/core/hive_location = null //Set to ref every time a core is built, for defining the hive location + + var/tier_slot_multiplier = 1 + var/larva_gestation_multiplier = 1 + var/bonus_larva_spawn_chance = 1 + var/hijack_burrowed_surge = FALSE //at hijack, start spawning lots of burrowed + /// how many burrowed is going to spawn during larva surge + var/hijack_burrowed_left = 0 + + var/dynamic_evolution = TRUE + var/evolution_rate = 3 // Only has use if dynamic_evolution is false + var/evolution_bonus = 0 + + var/allow_no_queen_actions = FALSE + var/allow_no_queen_evo = FALSE + var/evolution_without_ovipositor = TRUE //Temporary for the roundstart. + /// Set to false if you want to prevent evolutions into Queens + var/allow_queen_evolve = TRUE + /// Set to true if you want to prevent bursts and spawns of new xenos. Will also prevent healing if the queen no longer exists + var/hardcore = FALSE + /// Set to false if you want to prevent getting burrowed larva from latejoin marines + var/latejoin_burrowed = TRUE + /// If hit limit of larva from pylons + var/hit_larva_pylon_limit = FALSE + + var/see_humans_on_tacmap = FALSE + + var/list/hive_inherant_traits + + // Cultist Info + var/mob/living/carbon/leading_cult_sl + + //List of how many maximum of each special structure you can have + var/list/hive_structures_limit = list( + XENO_STRUCTURE_CORE = 1, + XENO_STRUCTURE_CLUSTER = 8, + XENO_STRUCTURE_EGGMORPH = 6, + XENO_STRUCTURE_RECOVERY = 6, + XENO_STRUCTURE_PYLON = 2, + ) + + var/global/list/hive_structure_types = list( + XENO_STRUCTURE_CORE = /datum/construction_template/xenomorph/core, + XENO_STRUCTURE_CLUSTER = /datum/construction_template/xenomorph/cluster, + XENO_STRUCTURE_EGGMORPH = /datum/construction_template/xenomorph/eggmorph, + XENO_STRUCTURE_RECOVERY = /datum/construction_template/xenomorph/recovery + ) + + var/list/list/hive_structures = list() //Stringref list of structures that have been built + var/list/list/hive_constructions = list() //Stringref list of structures that are being built + + var/datum/hive_status_ui/hive_ui + var/datum/mark_menu_ui/mark_ui + var/datum/hive_faction_ui/faction_ui + + var/list/tunnels = list() + + var/list/allies = list() + + var/list/resin_marks = list() + + var/list/banished_ckeys = list() + + var/hivecore_cooldown = FALSE + + var/need_round_end_check = FALSE + + //Joining as Facehugger vars + /// When can huggers join the round + var/hugger_timelock = 15 MINUTES + /// How many huggers can the hive support + var/playable_hugger_limit = 0 + /// Minimum number of huggers available at any hive size + var/playable_hugger_minimum = 2 + /// This number divides the total xenos counted for slots to give the max number of facehuggers + var/playable_hugger_max_divisor = 4 + + /// How many lesser drones the hive can support + var/lesser_drone_limit = 0 + /// Slots available for lesser drones will never go below this number + var/lesser_drone_minimum = 2 + /// This number divides the total xenos counted for slots to give the max number of lesser drones + var/playable_lesser_drones_max_divisor = 3 + + var/datum/tacmap/drawing/xeno/tacmap + var/minimap_type = MINIMAP_FLAG_XENO + + var/list/available_nicknumbers = list() + + /*Stores the image()'s for the xeno evolution radial menu + To add an image for your caste - add an icon to icons/mob/xenos/radial_xenos.dmi + Icon size should be 32x32, to make them fit within the radial menu border size your icon 22x22 and leave 10px transparent border. + The name of the icon should be the same as the XENO_CASTE_ define for that caste eg. #define XENO_CASTE_DRONE "Drone" + */ + var/static/list/evolution_menu_images + +/datum/faction_module/hive/New() + hive_ui = new(src) + mark_ui = new(src) + faction_ui = new(src) + minimap_type = get_minimap_flag_for_faction(hivenumber) + tacmap = new(src, minimap_type) + if(!internal_faction) + internal_faction = name + for(var/number in 1 to 999) + available_nicknumbers += number + if(hivenumber != XENO_HIVE_NORMAL) + return + + if(!evolution_menu_images) + evolution_menu_images = list() + generate_evo_menu_images() + + RegisterSignal(SSdcs, COMSIG_GLOB_POST_SETUP, PROC_REF(post_setup)) + +///Generate the image()'s requried for the evolution radial menu. +/datum/faction_module/hive/proc/generate_evo_menu_images() + for(var/datum/caste_datum/caste as anything in subtypesof(/datum/caste_datum)) + evolution_menu_images[initial(caste.caste_type)] = image('icons/mob/xenos/radial_xenos.dmi', initial(caste.caste_type)) + +/datum/faction_module/hive/proc/post_setup() + SIGNAL_HANDLER + + setup_evolution_announcements() + setup_pylon_limits() + +/datum/faction_module/hive/proc/setup_evolution_announcements() + for(var/time in GLOB.xeno_evolve_times) + if(time == "0") + continue + + addtimer(CALLBACK(src, PROC_REF(announce_evolve_available), GLOB.xeno_evolve_times[time]), text2num(time)) + +/// Sets up limits on pylons in New() for potential futureproofing with more static comms +/datum/faction_module/hive/proc/setup_pylon_limits() + hive_structures_limit[XENO_STRUCTURE_PYLON] = length(GLOB.all_static_telecomms_towers) || 2 + +/datum/faction_module/hive/proc/announce_evolve_available(list/datum/caste_datum/available_castes) + + var/list/castes_available = list() + for(var/datum/caste_datum/current_caste as anything in available_castes) + castes_available += initial(current_caste.caste_type) + + var/castes = castes_available.Join(", ") + xeno_message(SPAN_XENOANNOUNCE("The Hive is now strong enough to support: [castes]")) + xeno_maptext("The Hive can now support: [castes]", "Hive Strengthening") + + +// Adds a xeno to this hive +/datum/faction_module/hive/proc/add_xeno(mob/living/carbon/xenomorph/X) + if(!X || !istype(X)) + return + + // If the xeno is part of another hive, they should be removed from that one first + if(X.hive && X.hive != src) + X.hive.remove_xeno(X, TRUE) + + // Already in the hive + if(X in totalXenos) + return + + // Can only have one queen. + if(isqueen(X)) + if(!living_xeno_queen && !should_block_game_interaction(X)) // Don't consider xenos in admin level + set_living_xeno_queen(X) + + X.hivenumber = hivenumber + X.hive = src + + X.set_faction(internal_faction) + + if(X.hud_list) + X.hud_update() + + var/area/A = get_area(X) + if(!should_block_game_interaction(X) || (A.flags_atom & AREA_ALLOW_XENO_JOIN)) + totalXenos += X + if(X.tier == 2) + tier_2_xenos += X + else if(X.tier == 3) + tier_3_xenos += X + + // Xenos are a fuckfest of cross-dependencies of different datums that are initialized at different times + // So don't even bother trying updating UI here without large refactors + +// Removes the xeno from the hive +/datum/faction_module/hive/proc/remove_xeno(mob/living/carbon/xenomorph/xeno, hard = FALSE, light_mode = FALSE) + if(!xeno || !istype(xeno)) + return + + // Make sure the xeno was in the hive in the first place + if(!(xeno in totalXenos)) + return + + // This might be a redundant check now that Queen/Destroy() checks, but doesn't hurt to double check + if(living_xeno_queen == xeno) + var/mob/living/carbon/xenomorph/queen/next_queen = null + for(var/mob/living/carbon/xenomorph/queen/queen in totalXenos) + if(!should_block_game_interaction(queen) && queen != src && !QDELETED(queen)) + next_queen = queen + break + + set_living_xeno_queen(next_queen) // either null or a queen + + // We allow "soft" removals from the hive (the xeno still retains information about the hive) + // This is so that xenos can add themselves back to the hive if they should die or otherwise go "on leave" from the hive + if(hard) + xeno.hivenumber = 0 + xeno.hive = null +#ifndef UNIT_TESTS // Since this is a hard ref, we shouldn't confuse create_and_destroy + else + total_dead_xenos += xeno +#endif + + totalXenos -= xeno + if(xeno.tier == 2) + tier_2_xenos -= xeno + else if(xeno.tier == 3) + tier_3_xenos -= xeno + + // Only handle free slots if the xeno is not in tdome + if(!should_block_game_interaction(xeno)) + var/selected_caste = GLOB.xeno_datum_list[xeno.caste_type]?.type + if(used_slots[selected_caste]) + used_slots[selected_caste]-- + + if(!light_mode) + hive_ui.update_xeno_counts() + hive_ui.xeno_removed(xeno) + +/datum/faction_module/hive/proc/set_living_xeno_queen(mob/living/carbon/xenomorph/queen/queen) + if(!queen) + SStracking.delete_leader("hive_[hivenumber]") + SStracking.stop_tracking("hive_[hivenumber]", living_xeno_queen) + SShive_status.wait = 10 SECONDS + else + SStracking.set_leader("hive_[hivenumber]", queen) + SShive_status.wait = 2 SECONDS + + SEND_SIGNAL(src, COMSIG_HIVE_NEW_QUEEN, queen) + living_xeno_queen = queen + + recalculate_hive() + +/datum/faction_module/hive/proc/recalculate_hive() + //No leaders for a Hive without a Queen! + queen_leader_limit = living_xeno_queen ? 4 : 0 + + if (xeno_leader_list.len > queen_leader_limit) + var/diff = 0 + for (var/i in queen_leader_limit + 1 to xeno_leader_list.len) + if(!open_xeno_leader_positions.Remove(i)) + remove_hive_leader(xeno_leader_list[i]) + diff++ + xeno_leader_list.len -= diff // Changing the size of xeno_leader_list needs to go at the end or else it won't iterate through the list properly + else if (xeno_leader_list.len < queen_leader_limit) + for (var/i in xeno_leader_list.len + 1 to queen_leader_limit) + open_xeno_leader_positions += i + xeno_leader_list.len++ + + hive_ui.update_all_data() + +/datum/faction_module/hive/proc/add_hive_leader(mob/living/carbon/xenomorph/xeno) + if(!xeno) + return FALSE //How did this even happen? + if(!open_xeno_leader_positions.len) + return FALSE //Too many leaders already (no available xeno leader positions) + if(xeno.hive_pos != NORMAL_XENO) + return FALSE //Already on the list + var/leader_num = open_xeno_leader_positions[1] + xeno_leader_list[leader_num] = xeno + xeno.hive_pos = XENO_LEADER_HIVE_POS(leader_num) + xeno.handle_xeno_leader_pheromones() + xeno.hud_update() // To add leader star + open_xeno_leader_positions -= leader_num + + xeno.update_minimap_icon() + + give_action(xeno, /datum/action/xeno_action/activable/info_marker) + + hive_ui.update_xeno_keys() + return TRUE + +/datum/faction_module/hive/proc/remove_hive_leader(mob/living/carbon/xenomorph/xeno, light_mode = FALSE) + if(!istype(xeno) || !IS_XENO_LEADER(xeno)) + return FALSE + + var/leader_num = GET_XENO_LEADER_NUM(xeno) + + xeno_leader_list[leader_num] = null + + if(!light_mode) // Don't run side effects during deletions. Better yet, replace all this by signals someday + xeno.hive_pos = NORMAL_XENO + xeno.handle_xeno_leader_pheromones() + xeno.hud_update() // To remove leader star + + // Need to maintain ascending order of open_xeno_leader_positions + for (var/i in 1 to queen_leader_limit) + if (i > open_xeno_leader_positions.len || open_xeno_leader_positions[i] > leader_num) + open_xeno_leader_positions.Insert(i, leader_num) + break + + if(!light_mode) + hive_ui.update_xeno_keys() + + for(var/obj/effect/alien/resin/marker/leaderless_mark in resin_marks) //no resin_mark limit abuse + if(leaderless_mark.createdby == xeno.nicknumber) + qdel(leaderless_mark) + + xeno.update_minimap_icon() + + remove_action(xeno, /datum/action/xeno_action/activable/info_marker) + + return TRUE + +/datum/faction_module/hive/proc/replace_hive_leader(mob/living/carbon/xenomorph/original, mob/living/carbon/xenomorph/replacement) + if(!replacement || replacement.hive_pos != NORMAL_XENO) + return remove_hive_leader(original) + + var/leader_num = GET_XENO_LEADER_NUM(original) + + xeno_leader_list[leader_num] = replacement + + original.hive_pos = NORMAL_XENO + original.handle_xeno_leader_pheromones() + original.hud_update() // To remove leader star + remove_action(original, /datum/action/xeno_action/activable/info_marker) + + replacement.hive_pos = XENO_LEADER_HIVE_POS(leader_num) + replacement.handle_xeno_leader_pheromones() + replacement.hud_update() // To add leader star + give_action(replacement, /datum/action/xeno_action/activable/info_marker) + + hive_ui.update_xeno_keys() + +/datum/faction_module/hive/proc/handle_xeno_leader_pheromones() + for(var/mob/living/carbon/xenomorph/L in xeno_leader_list) + L.handle_xeno_leader_pheromones() + +/* + * Helper procs for the Hive Status UI + * These are all called by the hive status UI manager to update its data + */ + +// Returns a list of how many of each caste of xeno there are, sorted by tier +/datum/faction_module/hive/proc/get_xeno_counts() + // Every caste is manually defined here so you get + var/list/xeno_counts = list( + // Yes, Queen is technically considered to be tier 0 + list(XENO_CASTE_LARVA = 0, "Queen" = 0), + list(XENO_CASTE_DRONE = 0, XENO_CASTE_RUNNER = 0, XENO_CASTE_SENTINEL = 0, XENO_CASTE_DEFENDER = 0), + list(XENO_CASTE_HIVELORD = 0, XENO_CASTE_BURROWER = 0, XENO_CASTE_CARRIER = 0, XENO_CASTE_LURKER = 0, XENO_CASTE_SPITTER = 0, XENO_CASTE_WARRIOR = 0), + list(XENO_CASTE_BOILER = 0, XENO_CASTE_CRUSHER = 0, XENO_CASTE_PRAETORIAN = 0, XENO_CASTE_RAVAGER = 0) + ) + + for(var/mob/living/carbon/xenomorph/X in totalXenos) + //don't show xenos in the thunderdome when admins test stuff. + if(should_block_game_interaction(X)) + var/area/A = get_area(X) + if(!(A.flags_atom & AREA_ALLOW_XENO_JOIN)) + continue + + if(X.caste && X.counts_for_slots) + xeno_counts[X.caste.tier+1][X.caste.caste_type]++ + + return xeno_counts + +// Returns a sorted list of some basic info (stuff that's needed for sorting) about all the xenos in the hive +// The idea is that we sort this list, and use it as a "key" for all the other information (especially the nicknumber) +// in the hive status UI. That way we can minimize the amount of sorts performed by only calling this when xenos are created/disposed +/datum/faction_module/hive/proc/get_xeno_keys() + var/list/xenos = list() + + for(var/mob/living/carbon/xenomorph/X in totalXenos) + if(should_block_game_interaction(X)) + var/area/A = get_area(X) + if(!(A.flags_atom & AREA_ALLOW_XENO_JOIN)) + continue + + if(!(X in GLOB.living_xeno_list)) + continue + + // This looks weird, but in DM adding List A to List B actually adds each item in List B to List A, not List B itself. + // Having a nested list like this sort of tricks it into adding the list instead. + // In this case this results in an array of different 'xeno' dictionaries, rather than just a dictionary. + xenos += list(list( + "nicknumber" = X.nicknumber, + "tier" = X.tier, // This one is only important for sorting + "is_leader" = (IS_XENO_LEADER(X)), + "is_queen" = istype(X.caste, /datum/caste_datum/queen), + "caste_type" = X.caste_type + )) + + // Make it all nice and fancy by sorting the list before returning it + var/list/sorted_keys = sort_xeno_keys(xenos) + if(length(sorted_keys)) + return sorted_keys + return xenos + +// This sorts the xeno info list by multiple criteria. Prioritized in order: +// 1. Queen +// 2. Leaders +// 3. Tier +// It uses a slightly modified insertion sort to accomplish this +/datum/faction_module/hive/proc/sort_xeno_keys(list/xenos) + if(!length(xenos)) + return + + var/list/sorted_list = xenos.Copy() + + if(!length(sorted_list)) + return + + for(var/index in 2 to length(sorted_list)) + var/j = index + + while(j > 1) + var/current = sorted_list[j] + var/prev = sorted_list[j-1] + + // Queen comes first, always + if(current["is_queen"]) + sorted_list.Swap(j-1, j) + j-- + continue + + // don't muck up queen's slot + if(prev["is_queen"]) + j-- + continue + + // Leaders before normal xenos + if(!prev["is_leader"] && current["is_leader"]) + sorted_list.Swap(j-1, j) + j-- + continue + + // Make sure we're only comparing leaders to leaders and non-leaders to non-leaders when sorting + // This means we get leaders sorted first, then non-leaders sorted + // Sort by tier first, higher tiers over lower tiers, and then by name alphabetically + + // Could not think of an elegant way to write this + if(!(current["is_leader"]^prev["is_leader"])\ + && (prev["tier"] < current["tier"]\ + || prev["tier"] == current["tier"] && prev["caste_type"] > current["caste_type"]\ + )) + sorted_list.Swap(j-1, j) + + j-- + + return sorted_list + +// Returns a list with some more info about all xenos in the hive +/datum/faction_module/hive/proc/get_xeno_info() + var/list/xenos = list() + + for(var/mob/living/carbon/xenomorph/X in totalXenos) + if(should_block_game_interaction(X)) + var/area/A = get_area(X) + if(!(A.flags_atom & AREA_ALLOW_XENO_JOIN)) + continue + + var/xeno_name = X.name + // goddamn fucking larvas with their weird ass maturing system + // its name updates with its icon, unlike other castes which only update the mature/elder, etc. prefix on evolve + if(istype(X, /mob/living/carbon/xenomorph/larva)) + xeno_name = "Larva ([X.nicknumber])" + xenos["[X.nicknumber]"] = list( + "name" = xeno_name, + "strain" = X.get_strain_name(), + "ref" = "\ref[X]" + ) + + return xenos + +/datum/faction_module/hive/proc/set_hive_location(obj/effect/alien/resin/special/pylon/core/C) + if(!C || C == hive_location) + return + var/area/A = get_area(C) + xeno_message(SPAN_XENOANNOUNCE("The Queen has set the hive location as \the [A]."), 3, hivenumber) + hive_location = C + hive_ui.update_hive_location() + +// Returns a list of xeno healths and locations +/datum/faction_module/hive/proc/get_xeno_vitals() + var/list/xenos = list() + + for(var/mob/living/carbon/xenomorph/X in totalXenos) + if(should_block_game_interaction(X)) + var/area/A = get_area(X) + if(!(A.flags_atom & AREA_ALLOW_XENO_JOIN)) + continue + + if(!(X in GLOB.living_xeno_list)) + continue + + var/area/A = get_area(X) + var/area_name = "Unknown" + if(A) + area_name = A.name + + xenos["[X.nicknumber]"] = list( + "health" = round((X.health / X.maxHealth) * 100, 1), + "area" = area_name, + "is_ssd" = (!X.client) + ) + + return xenos + +#define TIER_3 "3" +#define TIER_2 "2" +#define OPEN_SLOTS "open_slots" +#define GUARANTEED_SLOTS "guaranteed_slots" + +// Returns an assoc list of open slots and guaranteed slots left +/datum/faction_module/hive/proc/get_tier_slots() + var/list/slots = list( + TIER_3 = list( + OPEN_SLOTS = 0, + GUARANTEED_SLOTS = list(), + ), + TIER_2 = list( + OPEN_SLOTS = 0, + GUARANTEED_SLOTS = list(), + ), + ) + + var/used_tier_2_slots = length(tier_2_xenos) + var/used_tier_3_slots = length(tier_3_xenos) + + for(var/caste_path in free_slots) + var/slots_free = free_slots[caste_path] + var/slots_used = used_slots[caste_path] + var/datum/caste_datum/current_caste = caste_path + if(slots_used) + // Don't count any free slots in use + switch(initial(current_caste.tier)) + if(2) + used_tier_2_slots -= min(slots_used, slots_free) + if(3) + used_tier_3_slots -= min(slots_used, slots_free) + if(slots_free <= slots_used) + continue + // Display any free slots available + switch(initial(current_caste.tier)) + if(2) + slots[TIER_2][GUARANTEED_SLOTS][initial(current_caste.caste_type)] = slots_free - slots_used + if(3) + slots[TIER_3][GUARANTEED_SLOTS][initial(current_caste.caste_type)] = slots_free - slots_used + + var/burrowed_factor = min(stored_larva, sqrt(4*stored_larva)) + var/effective_total = floor(burrowed_factor) + for(var/mob/living/carbon/xenomorph/xeno as anything in totalXenos) + if(xeno.counts_for_slots) + effective_total++ + + // Tier 3 slots are always 20% of the total xenos in the hive + slots[TIER_3][OPEN_SLOTS] = max(0, ceil(0.20*effective_total/tier_slot_multiplier) - used_tier_3_slots) + // Tier 2 slots are between 30% and 50% of the hive, depending + // on how many T3s there are. + slots[TIER_2][OPEN_SLOTS] = max(0, ceil(0.5*effective_total/tier_slot_multiplier) - used_tier_2_slots - used_tier_3_slots) + + return slots + +#undef TIER_3 +#undef TIER_2 +#undef OPEN_SLOTS +#undef GUARANTEED_SLOTS + +/datum/faction_module/hive/proc/can_build_structure(structure_name) + if(!structure_name || !hive_structures_limit[structure_name]) + return FALSE + if(get_structure_count(structure_name) >= hive_structures_limit[structure_name]) + return FALSE + return TRUE + +/datum/faction_module/hive/proc/get_structure_count(structure_name) + return length(hive_structures[structure_name]) + length(hive_constructions[structure_name]) + +/datum/faction_module/hive/proc/has_structure(structure_name) + if(!structure_name) + return FALSE + if(hive_structures[structure_name] && hive_structures[structure_name].len) + return TRUE + return FALSE + +/datum/faction_module/hive/proc/add_construction(obj/effect/alien/resin/construction/S) + if(!S || !S.template) + return FALSE + var/name_ref = initial(S.template.name) + if(!hive_constructions[name_ref]) + hive_constructions[name_ref] = list() + if(hive_constructions[name_ref].len >= hive_structures_limit[name_ref]) + return FALSE + hive_constructions[name_ref] += src + return TRUE + +/datum/faction_module/hive/proc/remove_construction(obj/effect/alien/resin/construction/S) + if(!S || !S.template) + return FALSE + var/name_ref = initial(S.template.name) + hive_constructions[name_ref] -= src + return TRUE + +/datum/faction_module/hive/proc/add_special_structure(obj/effect/alien/resin/special/S) + if(!S) + return FALSE + var/name_ref = initial(S.name) + if(!hive_structures[name_ref]) + hive_structures[name_ref] = list() + if(hive_structures[name_ref].len >= hive_structures_limit[name_ref]) + return FALSE + hive_structures[name_ref] += S + return TRUE + +/datum/faction_module/hive/proc/remove_special_structure(obj/effect/alien/resin/special/S) + if(!S) + return FALSE + var/name_ref = initial(S.name) + hive_structures[name_ref] -= S + return TRUE + +/datum/faction_module/hive/proc/has_special_structure(name_ref) + if(!name_ref || !hive_structures[name_ref] || !hive_structures[name_ref].len) + return 0 + return hive_structures[name_ref].len + +/datum/faction_module/hive/proc/abandon_on_hijack() + var/area/hijacked_dropship = get_area(living_xeno_queen) + var/shipside_humans_weighted_count = 0 + var/xenos_count = 0 + for(var/name_ref in hive_structures) + for(var/obj/effect/alien/resin/special/S in hive_structures[name_ref]) + if(get_area(S) == hijacked_dropship) + continue + S.hijack_delete = TRUE + hive_structures[name_ref] -= S + qdel(S) + for(var/mob/living/carbon/xenomorph/xeno as anything in totalXenos) + if(get_area(xeno) != hijacked_dropship && xeno.loc && is_ground_level(xeno.loc.z)) + if(isfacehugger(xeno) || islesserdrone(xeno)) + to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, you quickly find a hiding place to enter hibernation as you lose touch with the hive mind.")) + if(xeno.stomach_contents.len) + xeno.devour_timer = 0 + xeno.handle_stomach_contents() + qdel(xeno) + continue + if(xeno.hunter_data.hunted && !isqueen(xeno)) + to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, seperating you from her hive! You must defend yourself from the headhunter before you can enter hibernation...")) + xeno.set_hive_and_update(XENO_HIVE_FORSAKEN) + else + to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, you quickly find a hiding place to enter hibernation as you lose touch with the hive mind.")) + if(xeno.stomach_contents.len) + xeno.devour_timer = 0 + xeno.handle_stomach_contents() + qdel(xeno) + stored_larva++ + continue + if(xeno.tier >= 1) + xenos_count++ + for(var/i in GLOB.alive_mob_list) + var/mob/living/potential_host = i + if(!(potential_host.status_flags & XENO_HOST)) + continue + if(!is_ground_level(potential_host.z) || get_area(potential_host) == hijacked_dropship) + continue + var/obj/item/alien_embryo/A = locate() in potential_host + if(A && A.hivenumber != hivenumber) + continue + for(var/obj/item/alien_embryo/embryo in potential_host) + embryo.hivenumber = XENO_HIVE_FORSAKEN + potential_host.update_med_icon() + for(var/mob/living/carbon/human/current_human as anything in GLOB.alive_human_list) + if(!(isspecieshuman(current_human) || isspeciessynth(current_human))) + continue + var/datum/job/job = GLOB.RoleAuthority.roles_for_mode[current_human.job] + if(!job) + continue + var/turf/turf = get_turf(current_human) + if(is_mainship_level(turf?.z)) + shipside_humans_weighted_count += GLOB.RoleAuthority.calculate_role_weight(job) + hijack_burrowed_surge = TRUE + hijack_burrowed_left = max(ceil(shipside_humans_weighted_count * 0.5) - xenos_count, 5) + hivecore_cooldown = FALSE + xeno_message(SPAN_XENOBOLDNOTICE("The weeds have recovered! A new hive core can be built!"),3,hivenumber) + +/datum/faction_module/hive/proc/free_respawn(client/C) + stored_larva++ + if(!hive_location || !hive_location.spawn_burrowed_larva(C.mob)) + stored_larva-- + else + hive_ui.update_burrowed_larva() + +/datum/faction_module/hive/proc/respawn_on_turf(client/xeno_client, turf/spawning_turf) + var/mob/living/carbon/xenomorph/larva/new_xeno = spawn_hivenumber_larva(spawning_turf, hivenumber) + if(isnull(new_xeno)) + return FALSE + + if(!SSticker.mode.transfer_xeno(xeno_client.mob, new_xeno)) + qdel(new_xeno) + return FALSE + + new_xeno.visible_message(SPAN_XENODANGER("A larva suddenly emerges from a dead husk!"), + SPAN_XENOANNOUNCE("The hive has no core! You manage to emerge from your old husk as a larva!")) + msg_admin_niche("[key_name(new_xeno)] respawned at \a [spawning_turf]. [ADMIN_JMP(spawning_turf)]") + playsound(new_xeno, 'sound/effects/xeno_newlarva.ogg', 50, 1) + if(new_xeno.client?.prefs?.toggles_flashing & FLASH_POOLSPAWN) + window_flash(new_xeno.client) + + hive_ui.update_burrowed_larva() + +/datum/faction_module/hive/proc/do_buried_larva_spawn(mob/xeno_candidate) + var/spawning_area + if(hive_location) + spawning_area = hive_location + else if(living_xeno_queen) + spawning_area = living_xeno_queen + else for(var/mob/living/carbon/xenomorpheus as anything in totalXenos) + if(islarva(xenomorpheus) || isxeno_builder(xenomorpheus)) //next to xenos that should be in a safe spot + spawning_area = xenomorpheus + if(!spawning_area) + spawning_area = pick(totalXenos) // FUCK IT JUST GO ANYWHERE + var/list/turf_list + for(var/turf/open/open_turf in orange(3, spawning_area)) + if(istype(open_turf, /turf/open/space)) + continue + LAZYADD(turf_list, open_turf) + // just on the off-chance + if(!LAZYLEN(turf_list)) + return FALSE + var/turf/open/spawning_turf = pick(turf_list) + + var/mob/living/carbon/xenomorph/larva/new_xeno = spawn_hivenumber_larva(spawning_turf, hivenumber) + if(isnull(new_xeno)) + return FALSE + + if(!SSticker.mode.transfer_xeno(xeno_candidate, new_xeno)) + qdel(new_xeno) + return FALSE + new_xeno.visible_message(SPAN_XENODANGER("A larva suddenly burrows out of \the [spawning_turf]!"), + SPAN_XENODANGER("You burrow out of \the [spawning_turf] and awaken from your slumber. For the Hive!")) + msg_admin_niche("[key_name(new_xeno)] burrowed out from \a [spawning_turf]. [ADMIN_JMP(spawning_turf)]") + playsound(new_xeno, 'sound/effects/xeno_newlarva.ogg', 50, 1) + to_chat(new_xeno, SPAN_XENOANNOUNCE("You are a xenomorph larva awakened from slumber!")) + if(new_xeno.client) + if(new_xeno.client?.prefs?.toggles_flashing & FLASH_POOLSPAWN) + window_flash(new_xeno.client) + + stored_larva-- + hive_ui.update_burrowed_larva() + +/mob/living/proc/ally_of_hivenumber(hivenumber) + var/datum/faction_module/hive/indexed_hive = GLOB.hive_datum[hivenumber] + if(!indexed_hive) + return FALSE + + return indexed_hive.is_ally(src) + +/datum/faction_module/hive/proc/is_ally(mob/living/living_mob) + if(isxeno(living_mob)) + var/mob/living/carbon/xenomorph/zenomorf = living_mob + if(zenomorf.hivenumber == hivenumber) + return !zenomorf.banished + + if(!living_mob.faction) + return FALSE + + return faction_is_ally(living_mob.faction) + +/datum/faction_module/hive/proc/faction_is_ally(faction, ignore_queen_check = FALSE) + if(faction == internal_faction) + return TRUE + if(!ignore_queen_check && !living_xeno_queen) + return FALSE + + return allies[faction] + +/datum/faction_module/hive/proc/can_delay_round_end(mob/living/carbon/xenomorph/xeno) + if(HAS_TRAIT(src, TRAIT_NO_HIVE_DELAY)) + return FALSE + return TRUE + +/datum/faction_module/hive/proc/update_hugger_limit() + var/countable_xeno_iterator = 0 + for(var/mob/living/carbon/xenomorph/cycled_xeno as anything in totalXenos) + if(cycled_xeno.counts_for_slots) + countable_xeno_iterator++ + + playable_hugger_limit = max(floor(countable_xeno_iterator / playable_hugger_max_divisor), playable_hugger_minimum) + +/datum/faction_module/hive/proc/can_spawn_as_hugger(mob/dead/observer/user) + if(!GLOB.hive_datum || ! GLOB.hive_datum[hivenumber]) + return FALSE + if(jobban_isbanned(user, JOB_XENOMORPH)) // User is jobbanned + to_chat(user, SPAN_WARNING("You are banned from playing aliens and cannot spawn as a xenomorph.")) + return FALSE + if(world.time < hugger_timelock) + to_chat(user, SPAN_WARNING("The hive cannot support facehuggers yet...")) + return FALSE + if(world.time - user.timeofdeath < JOIN_AS_FACEHUGGER_DELAY) + var/time_left = floor((user.timeofdeath + JOIN_AS_FACEHUGGER_DELAY - world.time) / 10) + to_chat(user, SPAN_WARNING("You ghosted too recently. You cannot become a facehugger until 3 minutes have passed ([time_left] seconds remaining).")) + return FALSE + if(totalXenos.len <= 0) + //This is to prevent people from joining as Forsaken Huggers on the pred ship + to_chat(user, SPAN_WARNING("The hive has fallen, you can't join it!")) + return FALSE + for(var/mob_name in banished_ckeys) + if(banished_ckeys[mob_name] == user.ckey) + to_chat(user, SPAN_WARNING("You are banished from the [name], you may not rejoin unless the Queen re-admits you or dies.")) + return FALSE + + update_hugger_limit() + + var/current_hugger_count = 0 + for(var/mob/mob as anything in totalXenos) + if(isfacehugger(mob)) + current_hugger_count++ + if(playable_hugger_limit <= current_hugger_count) + to_chat(user, SPAN_WARNING("\The [GLOB.hive_datum[hivenumber]] cannot support more facehuggers! Limit: [current_hugger_count]/[playable_hugger_limit]")) + return FALSE + + if(tgui_alert(user, "Are you sure you want to become a facehugger?", "Confirmation", list("Yes", "No")) != "Yes") + return FALSE + + if(!user.client) + return FALSE + + return TRUE + +/datum/faction_module/hive/proc/get_current_playable_facehugger_count() + var/count = 0 + for(var/mob/mob as anything in totalXenos) + if(isfacehugger(mob)) + count++ + return count + +/datum/faction_module/hive/proc/spawn_as_hugger(mob/dead/observer/user, atom/A) + var/mob/living/carbon/xenomorph/facehugger/hugger = new /mob/living/carbon/xenomorph/facehugger(A.loc, null, hivenumber) + user.mind.transfer_to(hugger, TRUE) + hugger.visible_message(SPAN_XENODANGER("A facehugger suddenly emerges out of \the [A]!"), SPAN_XENODANGER("You emerge out of \the [A] and awaken from your slumber. For the Hive!")) + playsound(hugger, 'sound/effects/xeno_newlarva.ogg', 25, TRUE) + hugger.generate_name() + hugger.timeofdeath = user.timeofdeath // Keep old death time + +/datum/faction_module/hive/proc/update_lesser_drone_limit() + var/countable_xeno_iterator = 0 + for(var/mob/living/carbon/xenomorph/cycled_xeno as anything in totalXenos) + if(cycled_xeno.counts_for_slots) + countable_xeno_iterator++ + + lesser_drone_limit = max(floor(countable_xeno_iterator / playable_lesser_drones_max_divisor), lesser_drone_minimum) + +/datum/faction_module/hive/proc/can_spawn_as_lesser_drone(mob/dead/observer/user, obj/effect/alien/resin/special/pylon/spawning_pylon) + if(!GLOB.hive_datum || ! GLOB.hive_datum[hivenumber]) + return FALSE + + if(jobban_isbanned(user, JOB_XENOMORPH)) // User is jobbanned + to_chat(user, SPAN_WARNING("You are banned from playing aliens and cannot spawn as a xenomorph.")) + return FALSE + + if(world.time - user.timeofdeath < JOIN_AS_LESSER_DRONE_DELAY) + var/time_left = floor((user.timeofdeath + JOIN_AS_LESSER_DRONE_DELAY - world.time) / 10) + to_chat(user, SPAN_WARNING("You ghosted too recently. You cannot become a lesser drone until 30 seconds have passed ([time_left] seconds remaining).")) + return FALSE + + if(totalXenos.len <= 0) + to_chat(user, SPAN_WARNING("The hive has fallen, you can't join it!")) + return FALSE + + if(!living_xeno_queen) + to_chat(user, SPAN_WARNING("The selected hive does not have a Queen!")) + return FALSE + + if(spawning_pylon.lesser_drone_spawns < 1) + to_chat(user, SPAN_WARNING("The selected core or pylon does not have enough power for a lesser drone!")) + return FALSE + + update_lesser_drone_limit() + + var/current_lesser_drone_count = 0 + for(var/mob/mob as anything in totalXenos) + if(islesserdrone(mob)) + current_lesser_drone_count++ + + if(lesser_drone_limit <= current_lesser_drone_count) + to_chat(user, SPAN_WARNING("[GLOB.hive_datum[hivenumber]] cannot support more lesser drones! Limit: [current_lesser_drone_count]/[lesser_drone_limit]")) + return FALSE + + if(!user.client) + return FALSE + + return TRUE + +// Get amount of real xenos, don't count lessers/huggers +/datum/faction_module/hive/proc/get_real_total_xeno_count() + var/count = 0 + for(var/mob/living/carbon/xenomorph/xeno as anything in totalXenos) + if(xeno.counts_for_slots) + count++ + return count + +// Checks if we hit larva limit +/datum/faction_module/hive/proc/check_if_hit_larva_from_pylon_limit() + var/groundside_humans_weighted_count = 0 + for(var/mob/living/carbon/human/current_human as anything in GLOB.alive_human_list) + if(!(isspecieshuman(current_human) || isspeciessynth(current_human))) + continue + var/datum/job/job = GLOB.RoleAuthority.roles_for_mode[current_human.job] + if(!job) + continue + var/turf/turf = get_turf(current_human) + if(is_ground_level(turf?.z)) + groundside_humans_weighted_count += GLOB.RoleAuthority.calculate_role_weight(job) + hit_larva_pylon_limit = (get_real_total_xeno_count() + stored_larva) > (groundside_humans_weighted_count * ENDGAME_LARVA_CAP_MULTIPLIER) + hive_ui.update_pylon_status() + return hit_larva_pylon_limit + +///Called by /obj/item/alien_embryo when a host is bursting to determine extra larva per burst +/datum/faction_module/hive/proc/increase_larva_after_burst() + var/extra_per_burst = CONFIG_GET(number/extra_larva_per_burst) + partial_larva += extra_per_burst + convert_partial_larva_to_full_larva() + +///Called after times when partial larva are added to process them to stored larva +/datum/faction_module/hive/proc/convert_partial_larva_to_full_larva() + for(var/i = 1 to partial_larva) + partial_larva-- + stored_larva++ + +/datum/faction_module/hive/corrupted + name = "Corrupted Hive" + reporting_id = "corrupted" + hivenumber = XENO_HIVE_CORRUPTED + prefix = "Corrupted " + color = "#80ff80" + ui_color ="#4d994d" + latejoin_burrowed = FALSE + + need_round_end_check = TRUE + + var/list/defectors = list() + var/list/datum/weakref/personal_allies = list() + +/datum/faction_module/hive/corrupted/add_xeno(mob/living/carbon/xenomorph/xeno) + . = ..() + xeno.add_language(LANGUAGE_ENGLISH) + +/datum/faction_module/hive/corrupted/remove_xeno(mob/living/carbon/xenomorph/xeno, hard) + . = ..() + xeno.remove_language(LANGUAGE_ENGLISH) + +/datum/faction_module/hive/corrupted/can_delay_round_end(mob/living/carbon/xenomorph/xeno) + if(!faction_is_ally(FACTION_MARINE, TRUE)) + return TRUE + return FALSE + +/datum/faction_module/hive/alpha + name = "Alpha Hive" + reporting_id = "alpha" + hivenumber = XENO_HIVE_ALPHA + prefix = "Alpha " + color = "#ff4040" + ui_color = "#992626" + latejoin_burrowed = FALSE + + dynamic_evolution = FALSE + +/datum/faction_module/hive/bravo + name = "Bravo Hive" + reporting_id = "bravo" + hivenumber = XENO_HIVE_BRAVO + prefix = "Bravo " + color = "#ffff80" + ui_color = "#99994d" + latejoin_burrowed = FALSE + + dynamic_evolution = FALSE + +/datum/faction_module/hive/charlie + name = "Charlie Hive" + reporting_id = "charlie" + hivenumber = XENO_HIVE_CHARLIE + prefix = "Charlie " + color = "#bb40ff" + ui_color = "#702699" + latejoin_burrowed = FALSE + + dynamic_evolution = FALSE + +/datum/faction_module/hive/delta + name = "Delta Hive" + reporting_id = "delta" + hivenumber = XENO_HIVE_DELTA + prefix = "Delta " + color = "#8080ff" + ui_color = "#4d4d99" + latejoin_burrowed = FALSE + + dynamic_evolution = FALSE + +/datum/faction_module/hive/feral + name = "Feral Hive" + reporting_id = "feral" + hivenumber = XENO_HIVE_FERAL + prefix = "Feral " + color = "#828296" + ui_color = "#828296" + + construction_allowed = XENO_NOBODY + destruction_allowed = XENO_NOBODY + dynamic_evolution = FALSE + allow_no_queen_actions = TRUE + allow_no_queen_evo = TRUE + allow_queen_evolve = FALSE + latejoin_burrowed = FALSE + +/datum/faction_module/hive/forsaken + name = "Forsaken Hive" + reporting_id = "forsaken" + hivenumber = XENO_HIVE_FORSAKEN + prefix = "Forsaken " + color = "#cc8ec4" + ui_color = "#cc8ec4" + + dynamic_evolution = FALSE + allow_no_queen_actions = TRUE + allow_no_queen_evo = TRUE + allow_queen_evolve = FALSE + latejoin_burrowed = FALSE + + need_round_end_check = TRUE + +/datum/faction_module/hive/forsaken/can_delay_round_end(mob/living/carbon/xenomorph/xeno) + return FALSE + +/datum/faction_module/hive/tutorial + name = "Tutorial Hive" + reporting_id = "tutorial" + hivenumber = XENO_HIVE_TUTORIAL + prefix = "Inquisitive " + latejoin_burrowed = FALSE + + dynamic_evolution = FALSE + allow_queen_evolve = TRUE + evolution_without_ovipositor = FALSE + allow_no_queen_actions = TRUE + + ///Can have many tutorials going at once. + hive_structures_limit = list( + XENO_STRUCTURE_CORE = 999, + XENO_STRUCTURE_CLUSTER = 999, + XENO_STRUCTURE_EGGMORPH = 999, + XENO_STRUCTURE_RECOVERY = 999, + ) + +/datum/faction_module/hive/tutorial/can_delay_round_end(mob/living/carbon/xenomorph/xeno) + return FALSE + +/datum/faction_module/hive/yautja + name = "Hellhound Pack" + reporting_id = "hellhounds" + hivenumber = XENO_HIVE_YAUTJA + internal_faction = FACTION_YAUTJA + + dynamic_evolution = FALSE + allow_no_queen_actions = TRUE + allow_no_queen_evo = TRUE + allow_queen_evolve = FALSE + latejoin_burrowed = FALSE + + need_round_end_check = TRUE + +/datum/faction_module/hive/yautja/can_delay_round_end(mob/living/carbon/xenomorph/xeno) + return FALSE + +/datum/faction_module/hive/mutated + name = "Mutated Hive" + reporting_id = "mutated" + hivenumber = XENO_HIVE_MUTATED + prefix = "Mutated " + color = "#6abd99" + ui_color = "#6abd99" + + hive_inherant_traits = list(TRAIT_XENONID, TRAIT_NO_COLOR) + latejoin_burrowed = FALSE + +/datum/faction_module/hive/corrupted/tamed + name = "Tamed Hive" + reporting_id = "tamed" + hivenumber = XENO_HIVE_TAMED + prefix = "Tamed " + color = "#80ff80" + + dynamic_evolution = FALSE + allow_no_queen_actions = TRUE + allow_no_queen_evo = TRUE + allow_queen_evolve = FALSE + latejoin_burrowed = FALSE + + var/mob/living/carbon/human/leader + var/list/allied_factions + +/datum/faction_module/hive/corrupted/tamed/New() + . = ..() + hive_structures_limit[XENO_STRUCTURE_EGGMORPH] = 0 + +/datum/faction_module/hive/corrupted/tamed/proc/make_leader(mob/living/carbon/human/H) + if(!istype(H)) + return + + if(leader) + UnregisterSignal(leader, COMSIG_PARENT_QDELETING) + + leader = H + RegisterSignal(leader, COMSIG_PARENT_QDELETING, PROC_REF(handle_qdelete)) + +/datum/faction_module/hive/corrupted/tamed/proc/handle_qdelete(mob/living/carbon/human/H) + SIGNAL_HANDLER + + if(H == leader) + leader = null + + var/list/faction_groups = H.faction_group + if(faction_groups) + allied_factions = faction_groups.Copy() + if(!(H.faction in allied_factions)) + allied_factions += H.faction + +/datum/faction_module/hive/corrupted/tamed/add_xeno(mob/living/carbon/xenomorph/X) + . = ..() + X.faction_group = allied_factions + +/datum/faction_module/hive/corrupted/tamed/remove_xeno(mob/living/carbon/xenomorph/X, hard) + . = ..() + X.faction_group = list(X.faction) + +/datum/faction_module/hive/corrupted/tamed/is_ally(mob/living/carbon/C) + if(leader) + if(C.faction in leader.faction_group) + return TRUE + + if(C.faction == leader.faction) + return TRUE + else + if(C.faction in allied_factions) + return TRUE + + return ..() + +/datum/faction_module/hive/corrupted/renegade + name = "Renegade Hive" + reporting_id = "renegade" + hivenumber = XENO_HIVE_RENEGADE + prefix = "Renegade " + color = "#9c7a4d" + ui_color ="#80705c" + + dynamic_evolution = FALSE + allow_queen_evolve = FALSE + allow_no_queen_evo = TRUE + latejoin_burrowed = FALSE + +/datum/faction_module/hive/corrupted/renegade/New() + . = ..() + hive_structures_limit[XENO_STRUCTURE_EGGMORPH] = 0 + for(var/faction in FACTION_LIST_HUMANOID) //renegades allied to all humanoids, but it mostly affects structures. Their ability to attack humanoids and other xenos (including of the same hive) depends on iff settings + allies[faction] = TRUE + +/datum/faction_module/hive/corrupted/renegade/can_spawn_as_hugger(mob/dead/observer/user) + to_chat(user, SPAN_WARNING("The [name] cannot support facehuggers.")) + return FALSE + +/datum/faction_module/hive/corrupted/renegade/proc/iff_protection_check(mob/living/carbon/xenomorph/xeno, mob/living/carbon/attempt_harm_mob) + if(xeno == attempt_harm_mob) + return TRUE //you cannot hurt yourself... + if(!xeno.iff_tag) + return FALSE //can attack anyone if you don't have iff tag + if(isxeno(attempt_harm_mob)) + var/mob/living/carbon/xenomorph/target_xeno = attempt_harm_mob + if(!target_xeno.iff_tag) + return FALSE //can attack any xeno who don't have iff tag + for(var/faction in xeno.iff_tag.faction_groups) + if(faction in target_xeno.iff_tag.faction_groups) + return TRUE //cannot attack xenos with same iff setting + return FALSE + for(var/faction in xeno.iff_tag.faction_groups) + if(faction in attempt_harm_mob.faction_group) + return TRUE //cannot attack mob if iff is set to at least one of its factions + return FALSE + +/datum/faction_module/hive/corrupted/renegade/faction_is_ally(faction, ignore_queen_check = TRUE) + return ..() + +/datum/faction_module/hive/proc/on_queen_death() //break alliances on queen's death + if(allow_no_queen_actions || living_xeno_queen) + return + var/broken_alliances = FALSE + for(var/faction in allies) + if(!allies[faction]) + continue + change_stance(faction, FALSE) + broken_alliances = TRUE + + + if(broken_alliances) + xeno_message(SPAN_XENOANNOUNCE("With the death of the Queen, all alliances have been broken."), 3, hivenumber) + +/datum/faction_module/hive/proc/change_stance(faction, should_ally) + if(faction == name) + return + if(allies[faction] == should_ally) + return + allies[faction] = should_ally + + if(living_xeno_queen) + if(allies[faction]) + xeno_message(SPAN_XENOANNOUNCE("Our Queen set up an alliance with [faction]!"), 3, hivenumber) + else + xeno_message(SPAN_XENOANNOUNCE("Our Queen broke the alliance with [faction]!"), 3, hivenumber) + + for(var/number in GLOB.hive_datum) + var/datum/faction_module/hive/target_hive = GLOB.hive_datum[number] + if(target_hive.name != faction) + continue + if(!target_hive.living_xeno_queen && !target_hive.allow_no_queen_actions) + return + if(allies[faction]) + xeno_message(SPAN_XENOANNOUNCE("We sense that [name] [living_xeno_queen ? "Queen " : ""]set up an alliance with us!"), 3, target_hive.hivenumber) + return + + xeno_message(SPAN_XENOANNOUNCE("We sense that [name] [living_xeno_queen ? "Queen " : ""]broke the alliance with us!"), 3, target_hive.hivenumber) + if(target_hive.allies[name]) //autobreak alliance on betrayal + target_hive.change_stance(name, FALSE) + +/datum/faction_module/hive/corrupted/change_stance(faction, should_ally) + . = ..() + if(allies[faction]) + return + if(!(faction in FACTION_LIST_HUMANOID)) + return + + for(var/mob/living/carbon/xenomorph/xeno in totalXenos) // handle defecting xenos on betrayal + if(!xeno.iff_tag) + continue + if(!(faction in xeno.iff_tag.faction_groups)) + continue + if(xeno in defectors) + continue + if(xeno.caste_type == XENO_CASTE_QUEEN) + continue + INVOKE_ASYNC(src, PROC_REF(give_defection_choice), xeno, faction) + addtimer(CALLBACK(src, PROC_REF(handle_defectors), faction), 11 SECONDS) + +/datum/faction_module/hive/corrupted/proc/give_defection_choice(mob/living/carbon/xenomorph/xeno, faction) + if(tgui_alert(xeno, "Our Queen has broken the alliance with the [faction]. The device inside our carapace begins to suppress our connection with the Hive. Do we remove it and stay loyal to her?", "Alliance broken!", list("Stay loyal", "Obey the talls"), 10 SECONDS) == "Obey the talls") + if(!xeno.iff_tag) + to_chat(xeno, SPAN_XENOWARNING("It's too late now. The device is gone and our service to the Queen continues.")) + return + defectors += xeno + xeno.set_hive_and_update(XENO_HIVE_RENEGADE) + to_chat(xeno, SPAN_XENOANNOUNCE("You lost the connection with your Hive. Now you have no Queen, only your masters.")) + to_chat(xeno, SPAN_NOTICE("Your instincts have changed, you seem compelled to protect [english_list(xeno.iff_tag.faction_groups, "no one")].")) + return + xeno.visible_message(SPAN_XENOWARNING("[xeno] rips out [xeno.iff_tag]!"), SPAN_XENOWARNING("We rip out [xeno.iff_tag]! For the Hive!")) + xeno.adjustBruteLoss(50) + xeno.iff_tag.forceMove(get_turf(xeno)) + xeno.iff_tag = null + +/datum/faction_module/hive/corrupted/proc/handle_defectors(faction) + for(var/mob/living/carbon/xenomorph/xeno in totalXenos) + if(!xeno.iff_tag) + continue + if(xeno in defectors) + continue + if(!(faction in xeno.iff_tag.faction_groups)) + continue + xeno.visible_message(SPAN_XENOWARNING("[xeno] rips out [xeno.iff_tag]!"), SPAN_XENOWARNING("We rip out [xeno.iff_tag]! For the hive!")) + xeno.adjustBruteLoss(50) + xeno.iff_tag.forceMove(get_turf(xeno)) + xeno.iff_tag = null + if(!length(defectors)) + return + + xeno_message(SPAN_XENOANNOUNCE("We sense that [english_list(defectors)] turned their backs against their sisters and the Queen in favor of their slavemasters!"), 3, hivenumber) + defectors.Cut() + +/datum/faction_module/hive/corrupted/proc/add_personal_ally(mob/living/ally) + personal_allies += WEAKREF(ally) + ally.status_flags |= CORRUPTED_ALLY + ally.med_hud_set_status() + xeno_message(SPAN_XENOANNOUNCE("Our Queen proclaimed [ally] our ally! We must not harm them."), 3, hivenumber) + +/datum/faction_module/hive/corrupted/proc/remove_personal_ally(datum/weakref/ally_ref) + personal_allies -= ally_ref + var/mob/living/ally = ally_ref.resolve() + if(ally) + ally.status_flags &= ~CORRUPTED_ALLY + ally.med_hud_set_status() + xeno_message(SPAN_XENOANNOUNCE("Our Queen has declared that [ally] is no longer our ally!"), 3, hivenumber) + +/datum/faction_module/hive/corrupted/proc/clear_personal_allies(death = FALSE) + for(var/datum/weakref/ally_ref in personal_allies) + var/mob/living/ally = ally_ref.resolve() + if(!ally) + continue + ally.status_flags &= ~CORRUPTED_ALLY + ally.med_hud_set_status() + personal_allies.Cut() + if(!death) + xeno_message(SPAN_XENOANNOUNCE("Our Queen has broken all personal alliances with the talls! Favoritism is no more."), 3, hivenumber) + return + xeno_message(SPAN_XENOWARNING("With the death of the Queen, her friends no longer matter to us."), 3, hivenumber) + +/datum/faction_module/hive/corrupted/is_ally(mob/living/living_mob) + if(living_mob.status_flags & CORRUPTED_ALLY) + return TRUE + return ..() + +/datum/faction_module/hive/proc/override_evilution(evil, override) + if(SSxevolution) + SSxevolution.override_power(hivenumber, evil, override) + +/datum/faction_module/hive/corrupted/on_queen_death() + ..() + if(!length(personal_allies)) + return + clear_personal_allies(TRUE) + +//Xeno Resin Mark Shit, the very best place for it too :0) +//Defines at the bottom of this list here will show up at the top in the mark menu +/datum/xeno_mark_define + var/name = "xeno_declare" + var/icon_state = "empty" + var/desc = "Xenos make psychic markers with this meaning as positional lasting communication to eachother" + +/datum/xeno_mark_define/fortify + name = "Fortify" + desc = "Fortify this area!" + icon_state = "fortify" + +/datum/xeno_mark_define/weeds + name = "Need Weeds" + desc = "Need weeds here!" + icon_state = "weed" + +/datum/xeno_mark_define/nest + name = "Nest" + desc = "Nest enemies here!" + icon_state = "nest" + +/datum/xeno_mark_define/hosts + name = "Hosts" + desc = "Hosts here!" + icon_state = "hosts" + +/datum/xeno_mark_define/aide + name = "Aide" + desc = "Aide here!" + icon_state = "aide" + +/datum/xeno_mark_define/defend + name = "Defend" + desc = "Defend the hive here!" + icon_state = "defend" + +/datum/xeno_mark_define/danger + name = "Danger Warning" + desc = "Caution, danger here!" + icon_state = "danger" + +/datum/xeno_mark_define/rally + name = "Rally" + desc = "Group up here!" + icon_state = "rally" + +/datum/xeno_mark_define/hold + name = "Hold" + desc = "Hold this area!" + icon_state = "hold" + +/datum/xeno_mark_define/ambush + name = "Ambush" + desc = "Ambush the enemy here!" + icon_state = "ambush" +/datum/xeno_mark_define/attack + name = "Attack" + desc = "Attack the enemy here!" + icon_state = "attack" diff --git a/code/datums/factions/_modules/objectives.dm b/code/datums/factions/_modules/objectives.dm new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/datums/factions/clf.dm b/code/datums/factions/clf.dm index ce53b505b352..d6d010ddee11 100644 --- a/code/datums/factions/clf.dm +++ b/code/datums/factions/clf.dm @@ -1,15 +1,21 @@ /datum/faction/clf - name = "Colonial Liberation Front" - faction_tag = FACTION_CLF + name = NAME_FACTION_CLF + desc = "The Colonial Liberation Front is a paramilitary group primarily located in the Neroid sector of United Americas space. Their stated goal is to secure the independence of all of the colonies in the Neroid sector. They are the largest and most active militant group pushing for the independence of the colonies. \ + The United Americas government classifies the CLF as a terrorist organization, with membership in the organization or providing financial or material support for the CLF being prosecutable offenses. The CLF grew organically from several different groups that formed in the wake of the Slaughter of Xibou in 2164. Prior to the slaughter of Xibou the conflicts between the United Americas government and the colonists of the Neroid sector had remained mostly non-violent. \ + The sudden increase in tensions after Xibou, combined with the images of slaughtered fighters and civilians alike, greatly increased the willingness of the colonists to take up arms against the United Americas. Several different militant groups formed in the years following the Slaughter, and as they negotiated with one another and found common cause the CLF was formed from their union." + + faction_name = FACTION_CLF + faction_tag = SIDE_FACTION_CLF + relations_pregen = RELATIONS_FACTION_CLF /datum/faction/clf/modify_hud_holder(image/holder, mob/living/carbon/human/human) var/hud_icon_state - var/obj/item/card/id/ID = human.get_idcard() + var/obj/item/card/id/id_card = human.get_idcard() var/_role if(human.mind) _role = human.job - else if(ID) - _role = ID.rank + else if(id_card) + _role = id_card.rank switch(_role) if(JOB_CLF_ENGI) hud_icon_state = "engi" diff --git a/code/datums/factions/colonists.dm b/code/datums/factions/colonists.dm new file mode 100644 index 000000000000..aeb0ac337e33 --- /dev/null +++ b/code/datums/factions/colonists.dm @@ -0,0 +1,42 @@ +/datum/faction/colonist + name = NAME_FACTION_COLONIST + desc = "Colonists, the most ordinary citizens of the colonies, ADDITIONAL INFORMATION IS CHANGED" + + faction_name = FACTION_COLONIST + faction_iff_tag_type = /obj/item/faction_tag/colonist + + role_mappings = list( + MODE_NAME_EXTENDED = list(), + MODE_NAME_DISTRESS_SIGNAL = list(), + MODE_NAME_FACTION_CLASH = list(), + MODE_NAME_CRASH = list(), + MODE_NAME_WISKEY_OUTPOST = list(), + MODE_NAME_HUNTER_GAMES = list(), + MODE_NAME_HIVE_WARS = list(), + MODE_NAME_INFECTION = list() + ) + roles_list = list( + MODE_NAME_EXTENDED = ROLES_REGULAR_SURV, + MODE_NAME_DISTRESS_SIGNAL = ROLES_REGULAR_SURV, + MODE_NAME_FACTION_CLASH = list(), + MODE_NAME_CRASH = ROLES_REGULAR_SURV, + MODE_NAME_WISKEY_OUTPOST = list(), + MODE_NAME_HUNTER_GAMES = list( + JOB_SURVIVOR + ), + MODE_NAME_HIVE_WARS = list(), + MODE_NAME_INFECTION = list( + JOB_SURVIVOR + ) + ) + + weight_act = list( + MODE_NAME_EXTENDED = FALSE, + MODE_NAME_DISTRESS_SIGNAL = FALSE, + MODE_NAME_FACTION_CLASH = FALSE, + MODE_NAME_CRASH = FALSE, + MODE_NAME_WISKEY_OUTPOST = FALSE, + MODE_NAME_HUNTER_GAMES = FALSE, + MODE_NAME_HIVE_WARS = FALSE, + MODE_NAME_INFECTION = FALSE + ) diff --git a/code/datums/factions/contractor.dm b/code/datums/factions/contractor.dm index 5e0f125b06b0..7b5e8106d0d9 100644 --- a/code/datums/factions/contractor.dm +++ b/code/datums/factions/contractor.dm @@ -1,3 +1,6 @@ /datum/faction/contractor - name = "Vanguard's Arrow Incorporated" - faction_tag = FACTION_CONTRACTOR + name = NAME_FACTION_CONTRACTOR + desc = "There is no information" + + faction_name = FACTION_CONTRACTOR + faction_iff_tag_type = /obj/item/faction_tag/contractor diff --git a/code/datums/factions/dutch's_dozen.dm b/code/datums/factions/dutch's_dozen.dm new file mode 100644 index 000000000000..455224371e13 --- /dev/null +++ b/code/datums/factions/dutch's_dozen.dm @@ -0,0 +1,6 @@ +/datum/faction/dutchs_dozen + name = NAME_FACTION_DUTCH + desc = "Like the Colonial Liberation Front and the better-equipped Freelancer Mercenaries before them, the Dozens start with non-standard UA weaponry, employing equipment of the United Americas such as the M16A2 assault rifle and the minigun. While the weapons on their own are outdated and cannot take most attachments, their automatic weaponry is equally deadly, meant for hunting their targets. \ + The Dozens will also expect a guerrilla style of gameplay, as unlike most modern forces of this era, the Dozens use special techniques related to survival and can handle themselves on the ground without support. Whether it would take place in a jungle landscape or in urban grounds. One advantage of the Dutch's Mercenary Team is their speed and aggressiveness, which are primarily used to eliminate their target(s), due to the target's equal amount of speed, aggressiveness and robustness." + + faction_name = FACTION_DUTCH diff --git a/code/datums/factions/faction.dm b/code/datums/factions/faction.dm index a6201da70467..fd581b399cfc 100644 --- a/code/datums/factions/faction.dm +++ b/code/datums/factions/faction.dm @@ -1,13 +1,973 @@ /datum/faction - var/name = "Neutral Faction" - var/faction_tag = FACTION_NEUTRAL + var/name = NAME_FACTION_NEUTRAL + var/desc = "Neutral Faction" + + var/faction_name = FACTION_NEUTRAL + var/faction_tag = SIDE_FACTION_NEUTRAL + + var/organ_faction_iff_tag_type + var/faction_iff_tag_type + + var/relations_pregen[] = RELATIONS_NEUTRAL + var/datum/faction_relations/relations_datum + var/hud_type = FACTION_HUD + var/orders = "Остаться в живых" + var/color = "#22888a" + var/ui_color = "#22888a" + var/prefix = "" + var/list/totalMobs = list() + var/list/totalDeadMobs = list() + var/list/faction_leaders = list() + var/list/late_join_landmarks = list() + var/mob/living/carbon/faction_leader + var/datum/tacmap/faction_datum/tcmp_faction_datum + var/datum/objectives_datum/objectives_controller + var/list/objectives = list() + var/faction_victory_points = 0 + var/homes_sector_occupation = TRUE + var/objectives_active = FALSE + var/need_round_end_check = FALSE + +//////////////// +//BALANCE DEFS// +//////////////// + var/list/role_mappings = list() + var/list/roles_list = list() + var/list/coefficient_per_role = list() + var/list/affected_spawns // TODO: СДЕЛАТЬ СКЕЙЛИНГ СПАВНА ВЕЩЕЙ В ВЕНДОРАХ И УСЛОЖНЕНИЯ ЗАДАЧ ОТ ОНЛАЙНА НАПРИМЕР КАК ОЧКИ ДЕФКОНА + var/weight_act = list( + MODE_NAME_EXTENDED = TRUE, + MODE_NAME_DISTRESS_SIGNAL = TRUE, + MODE_NAME_FACTION_CLASH = TRUE, + MODE_NAME_CRASH = TRUE, + MODE_NAME_WISKEY_OUTPOST = TRUE, + MODE_NAME_HUNTER_GAMES = TRUE, + MODE_NAME_HIVE_WARS = TRUE, + MODE_NAME_INFECTION = TRUE + ) + + var/spawning_enabled = TRUE + var/latejoin_enabled = TRUE + var/force_spawning = FALSE + + + var/datum/mark_menu_ui/mark_ui + var/datum/hive_status_ui/faction_ui + var/datum/faction_task_ui/task_interface + var/datum/objective_memory_storage/objective_memory + var/datum/objective_memory_interface/objective_interface + var/datum/research_objective_memory_interface/research_objective_interface + +////////////// +/datum/faction/New() + relations_datum = new(src) + tcmp_faction_datum = new(src) + objectives_controller = GLOB.objective_controller[faction_name] + task_interface = new(src) + objective_memory = new() + objective_interface = new() + research_objective_interface = new() + +/datum/faction/can_vv_modify() + return FALSE /datum/faction/proc/modify_hud_holder(image/holder, mob/living/carbon/human/H) return +/datum/faction/proc/add_mob(mob/living/carbon/carbon) + if(!carbon || !istype(carbon)) + return + + if(carbon.faction && carbon.faction != src) + carbon.faction.remove_mob(carbon, TRUE) + + if(carbon in totalMobs) + return + + carbon.faction = src + + if(carbon.hud_list) + carbon.hud_update() + + if(!carbon.statistic_exempt) + totalMobs += carbon + +/datum/faction/proc/remove_mob(mob/living/carbon/carbon, hard = FALSE) + if(!carbon || !istype(carbon)) + return + + if(!(carbon in totalMobs)) + return + + if(hard) + carbon.faction = null + else + totalDeadMobs += carbon + + totalMobs -= carbon + +/datum/faction/proc/can_delay_round_end(mob/living/carbon/carbon) + return TRUE + +/datum/faction/proc/update_hugger_limit() + playable_hugger_limit = 2 + Ceiling(totalMobs.len / 4) + +/datum/faction/proc/can_spawn_as_hugger(mob/dead/observer/user) + if(world.time < hugger_timelock) + to_chat(user, SPAN_WARNING("The hive cannot support facehuggers yet...")) + return FALSE + if(world.time - user.timeofdeath < JOIN_AS_FACEHUGGER_DELAY) + var/time_left = round((user.timeofdeath + JOIN_AS_FACEHUGGER_DELAY - world.time) / 10) + to_chat(user, SPAN_WARNING("You ghosted too recently. You cannot become a facehugger until 3 minutes have passed ([time_left] seconds remaining).")) + return FALSE + if(totalMobs.len <= 0) + //This is to prevent people from joining as Forsaken Huggers on the pred ship + to_chat(user, SPAN_WARNING("The hive has fallen, you can't join it!")) + return FALSE + + update_hugger_limit() + + var/current_hugger_count = 0 + for(var/mob/mob as anything in totalMobs) + if(isfacehugger(mob)) + current_hugger_count++ + if(playable_hugger_limit <= current_hugger_count) + to_chat(user, SPAN_WARNING("\The [name] cannot support more facehuggers! Limit: [current_hugger_count]/[playable_hugger_limit]")) + return FALSE + + if(alert(user, "Are you sure you want to become a facehugger?", usr.client.auto_lang(LANGUAGE_CONFIRM), usr.client.auto_lang(LANGUAGE_YES), usr.client.auto_lang(LANGUAGE_NO)) != usr.client.auto_lang(LANGUAGE_YES)) + return FALSE + return TRUE + +/datum/faction/proc/spawn_as_hugger(mob/dead/observer/user, atom/A) + var/mob/living/carbon/xenomorph/facehugger/facehugger = new /mob/living/carbon/xenomorph/facehugger(A.loc, null, src) + user.mind.transfer_to(facehugger, TRUE) + facehugger.visible_message(SPAN_XENODANGER("A facehugger suddenly emerges out of \the [A]!"), SPAN_XENODANGER("You emerge out of \the [A] and awaken from your slumber. For the Hive!")) + playsound(facehugger, 'sound/effects/xeno_newlarva.ogg', 25, TRUE) + facehugger.generate_name() + +//Ally procs +/atom/movable/proc/ally(datum/faction/ally_faction) + if(!ally_faction) + return FALSE + + var/list/factions = list() + factions += ally_faction + for(var/datum/faction/i in ally_faction.relations_datum.allies) + factions += i + if(isnull(factions) || !faction) + return FALSE + + return faction in factions + +/mob/ally(datum/faction/ally_faction) + if(!ally_faction) + return FALSE + + var/list/factions = list() + factions += ally_faction + for(var/datum/faction/i in ally_faction.relations_datum.allies) + factions += i + + if(isnull(factions) || !faction) + return FALSE + + return faction in factions + +/mob/living/carbon/ally(datum/faction/ally_faction) + if(!ally_faction) + return FALSE + + if(client in ally_faction.banished_ckeys) + return FALSE + + if((organ_faction_tag || (faction.faction_tag in SIDE_ORGANICAL_DOM)) && (ally_faction.faction_tag in SIDE_ORGANICAL_DOM)) + if(organ_faction_tag) + return ally_faction.organ_faction_tag_is_ally(organ_faction_tag) + else if(faction == ally_faction) + return TRUE + else if(faction_tag) + return ally_faction.faction_tag_is_ally(faction_tag) + + return FALSE + +/datum/faction/proc/organ_faction_tag_is_ally(obj/item/faction_tag/organ/FT) + if(FT.faction == src) + return TRUE + for(var/datum/faction/faction in FT.factions + FT.faction) + if(relations_datum.allies[faction.faction_name]) + return TRUE + + return FALSE + +/datum/faction/proc/faction_tag_is_ally(obj/item/faction_tag/FT) + if(FT.faction == src) + return TRUE + for(var/datum/faction/faction in FT.factions + FT.faction) + if(relations_datum.allies[faction.faction_name]) + return TRUE + else if(faction.faction_tag == faction_tag) + return TRUE + + return FALSE + +/datum/faction/proc/faction_is_ally(datum/faction/faction_to_check) + if(faction_to_check.faction_tag == faction_tag) + return TRUE + + if(relations_datum.allies[faction_to_check.faction_name]) + return TRUE + + return FALSE + /datum/faction/proc/get_antag_guns_snowflake_equipment() return list() /datum/faction/proc/get_antag_guns_sorted_equipment() return list() + + + + +////////////// +//XENO PROCS// +////////////// +/datum/faction/proc/set_living_xeno_queen(mob/living/carbon/xenomorph/queen/XQ) + if(XQ == null) + mutators.reset_mutators() + SStracking.delete_leader("hive_[faction_name]") + SStracking.stop_tracking("hive_[faction_name]", living_xeno_queen) + SShive_status.wait = 10 SECONDS + else + SStracking.set_leader("hive_[faction_name]", XQ) + SShive_status.wait = 2 SECONDS + + living_xeno_queen = XQ + mutators.xeno = XQ + + recalculate_hive() + +/datum/faction/proc/recalculate_hive() + if(!living_xeno_queen) + queen_leader_limit = 0 //No leaders for a Hive without a Queen! + else + queen_leader_limit = 4 + mutators.leader_count_boost + + if(xeno_leader_list.len > queen_leader_limit) + var/diff = 0 + for(var/i in queen_leader_limit + 1 to xeno_leader_list.len) + if(!open_xeno_leader_positions.Remove(i)) + remove_hive_leader(xeno_leader_list[i]) + diff++ + xeno_leader_list.len -= diff // Changing the size of xeno_leader_list needs to go at the end or else it won't iterate through the list properly + else if(xeno_leader_list.len < queen_leader_limit) + for (var/i in xeno_leader_list.len + 1 to queen_leader_limit) + open_xeno_leader_positions += i + xeno_leader_list.len++ + + + tier_slot_multiplier = mutators.tier_slot_multiplier + larva_gestation_multiplier = mutators.larva_gestation_multiplier + bonus_larva_spawn_chance = mutators.bonus_larva_spawn_chance + + faction_ui.update_all_data() + +/datum/faction/proc/add_hive_leader(mob/living/carbon/xenomorph/X) + if(!X) + return FALSE //How did this even happen? + if(!open_xeno_leader_positions.len) + return FALSE //Too many leaders already (no available xeno leader positions) + if(X.hive_pos != NORMAL_XENO) + return FALSE //Already on the list + var/leader_num = open_xeno_leader_positions[1] + xeno_leader_list[leader_num] = X + X.hive_pos = XENO_LEADER_HIVE_POS(leader_num) + X.handle_xeno_leader_pheromones() + X.hud_update() // To add leader star + open_xeno_leader_positions -= leader_num + + faction_ui.update_xeno_keys() + return TRUE + +/datum/faction/proc/remove_hive_leader(mob/living/carbon/xenomorph/X, light_mode = FALSE) + if(!istype(X) || !IS_XENO_LEADER(X)) + return FALSE + + var/leader_num = GET_XENO_LEADER_NUM(X) + + xeno_leader_list[leader_num] = null + + if(!light_mode) // Don't run side effects during deletions. Better yet, replace all this by signals someday + X.hive_pos = NORMAL_XENO + X.handle_xeno_leader_pheromones() + X.hud_update() // To remove leader star + + // Need to maintain ascending order of open_xeno_leader_positions + for (var/i in 1 to queen_leader_limit) + if(i > open_xeno_leader_positions.len || open_xeno_leader_positions[i] > leader_num) + open_xeno_leader_positions.Insert(i, leader_num) + break + + if(!light_mode) + faction_ui.update_xeno_keys() + + return TRUE + +/datum/faction/proc/replace_hive_leader(mob/living/carbon/xenomorph/original, mob/living/carbon/xenomorph/replacement) + if(!replacement || replacement.hive_pos != NORMAL_XENO) + return remove_hive_leader(original) + + var/leader_num = GET_XENO_LEADER_NUM(original) + + xeno_leader_list[leader_num] = replacement + + original.hive_pos = NORMAL_XENO + original.handle_xeno_leader_pheromones() + original.hud_update() // To remove leader star + + replacement.hive_pos = XENO_LEADER_HIVE_POS(leader_num) + replacement.handle_xeno_leader_pheromones() + replacement.hud_update() // To add leader star + + faction_ui.update_xeno_keys() + +/datum/faction/proc/handle_xeno_leader_pheromones() + for(var/mob/living/carbon/xenomorph/L in xeno_leader_list) + L.handle_xeno_leader_pheromones() + +/* + * Helper procs for the Hive Status UI + * These are all called by the hive status UI manager to update its data + */ + +// Returns a list of how many of each caste of xeno there are, sorted by tier +/datum/faction/proc/get_xeno_counts() + // Every caste is manually defined here so you get + var/list/xeno_counts = list( + // Yes, Queen is technically considered to be tier 0 + list(XENO_CASTE_LARVA = 0, "Queen" = 0), + list(XENO_CASTE_DRONE = 0, XENO_CASTE_RUNNER = 0, XENO_CASTE_SENTINEL = 0, XENO_CASTE_DEFENDER = 0), + list(XENO_CASTE_HIVELORD = 0, XENO_CASTE_BURROWER = 0, XENO_CASTE_CARRIER = 0, XENO_CASTE_LURKER = 0, XENO_CASTE_SPITTER = 0, XENO_CASTE_WARRIOR = 0), + list(XENO_CASTE_BOILER = 0, XENO_CASTE_CRUSHER = 0, XENO_CASTE_PRAETORIAN = 0, XENO_CASTE_RAVAGER = 0) + ) + + for(var/mob/living/carbon/xenomorph/X in totalMobs) + //don't show xenos in the thunderdome when admins test stuff. + if(X.statistic_exempt) + continue + + if(X.caste) + xeno_counts[X.caste.tier+1][X.caste.caste_type]++ + + return xeno_counts + +// Returns a sorted list of some basic info (stuff that's needed for sorting) about all the xenos in the hive +// The idea is that we sort this list, and use it as a "key" for all the other information (especially the nicknumber) +// in the hive status UI. That way we can minimize the amount of sorts performed by only calling this when xenos are created/disposed +/datum/faction/proc/get_xeno_keys() + var/list/xenos[totalMobs.len] + + var/index = 1 + var/useless_slots = 0 + for(var/mob/living/carbon/xenomorph/X in totalMobs) + if(X.statistic_exempt) + useless_slots++ + continue + + // Insert without doing list merging + xenos[index++] = list( + "nicknumber" = X.nicknumber, + "tier" = X.tier, // This one is only important for sorting + "is_leader" = (IS_XENO_LEADER(X)), + "is_queen" = istype(X.caste, /datum/caste_datum/queen), + "caste_type" = X.caste_type + ) + + // Clear nulls from the xenos list + xenos.len -= useless_slots + + // Make it all nice and fancy by sorting the list before returning it + var/list/sorted_keys = sort_xeno_keys(xenos) + if(length(sorted_keys)) + return sorted_keys + return xenos + +// This sorts the xeno info list by multiple criteria. Prioritized in order: +// 1. Queen +// 2. Leaders +// 3. Tier +// It uses a slightly modified insertion sort to accomplish this +/datum/faction/proc/sort_xeno_keys(list/xenos) + if(!length(xenos)) + return + + var/list/sorted_list = xenos.Copy() + + if(!length(sorted_list)) + return + + for(var/index in 2 to length(sorted_list)) + var/j = index + + while(j > 1) + var/current = sorted_list[j] + var/prev = sorted_list[j-1] + + // Queen comes first, always + if(current["is_queen"]) + sorted_list.Swap(j-1, j) + j-- + continue + + // don't muck up queen's slot + if(prev["is_queen"]) + j-- + continue + + // Leaders before normal xenos + if(!prev["is_leader"] && current["is_leader"]) + sorted_list.Swap(j-1, j) + j-- + continue + + // Make sure we're only comparing leaders to leaders and non-leaders to non-leaders when sorting + // This means we get leaders sorted first, then non-leaders sorted + // Sort by tier first, higher tiers over lower tiers, and then by name alphabetically + + // Could not think of an elegant way to write this + if(!(current["is_leader"]^prev["is_leader"])\ + && (prev["tier"] < current["tier"]\ + || prev["tier"] == current["tier"] && prev["caste_type"] > current["caste_type"]\ + )) + sorted_list.Swap(j-1, j) + + j-- + + return sorted_list + +// Returns a list with some more info about all xenos in the hive +/datum/faction/proc/get_xeno_info() + var/list/xenomorphs = list() + + for(var/mob/living/carbon/xenomorph/xenomorph in totalMobs) + if(xenomorph.statistic_exempt) + continue + + var/xeno_name = xenomorph.name + // goddamn fucking larvas with their weird ass maturing system + // its name updates with its icon, unlike other castes which only update the mature/elder, etc. prefix on evolve + if(istype(xenomorph, /mob/living/carbon/xenomorph/larva)) + xeno_name = "Larva ([xenomorph.nicknumber])" + xenomorphs["[xenomorph.nicknumber]"] = list( + "name" = xeno_name, + "strain" = xenomorph.mutation_type, + "ref" = "\ref[xenomorph]" + ) + + return xenomorphs + +/datum/faction/proc/set_faction_location(obj/effect/alien/resin/special/pylon/core/core) + if(!core || core == faction_location) + return + var/area/A = get_area(core) + xeno_message(SPAN_XENOANNOUNCE("The Queen has set the hive location as \the [A]."), 3, src) + faction_location = core + faction_ui.update_faction_location() + +// Returns a list of xeno healths and locations +/datum/faction/proc/get_xeno_vitals() + var/list/xenomorphs = list() + + for(var/mob/living/carbon/xenomorph/xenomorph in totalMobs) + if(xenomorph.statistic_exempt) + continue + + if(!(xenomorph in GLOB.living_xeno_list)) + continue + + var/area/area = get_area(xenomorph) + var/area_name = "Unknown" + if(area) + area_name = area.name + + xenomorphs["[xenomorph.nicknumber]"] = list( + "health" = round((xenomorph.health / xenomorph.maxHealth) * 100, 1), + "area" = area_name, + "is_ssd" = (!xenomorph.client) + ) + + return xenomorphs + +#define TIER_3 "3" +#define TIER_2 "2" +#define OPEN_SLOTS "open_slots" +#define GUARANTEED_SLOTS "guaranteed_slots" + +// Returns an assoc list of open slots and guaranteed slots left +/datum/faction/proc/get_tier_slots() + var/list/slots = list( + TIER_3 = list( + OPEN_SLOTS = 0, + GUARANTEED_SLOTS = list(), + ), + TIER_2 = list( + OPEN_SLOTS = 0, + GUARANTEED_SLOTS = list(), + ), + ) + + var/used_tier_2_slots = length(tier_2_xenos) + var/used_tier_3_slots = length(tier_3_xenos) + + for(var/caste_path in free_slots) + var/slots_free = free_slots[caste_path] + var/slots_used = used_slots[caste_path] + var/datum/caste_datum/current_caste = caste_path + if(slots_used) + // Don't count any free slots in use + switch(initial(current_caste.tier)) + if(2) + used_tier_2_slots -= min(slots_used, slots_free) + if(3) + used_tier_3_slots -= min(slots_used, slots_free) + if(slots_free <= slots_used) + continue + // Display any free slots available + switch(initial(current_caste.tier)) + if(2) + slots[TIER_2][GUARANTEED_SLOTS][initial(current_caste.caste_type)] = slots_free - slots_used + if(3) + slots[TIER_3][GUARANTEED_SLOTS][initial(current_caste.caste_type)] = slots_free - slots_used + + var/burrowed_factor = min(stored_larva, sqrt(4*stored_larva)) + var/effective_total = round(burrowed_factor) + for(var/mob/living/carbon/xenomorph/xeno as anything in totalMobs) + if(xeno.counts_for_slots) + effective_total++ + + // Tier 3 slots are always 20% of the total xenos in the hive + slots[TIER_3][OPEN_SLOTS] = max(0, Ceiling(0.20*effective_total/tier_slot_multiplier) - used_tier_3_slots) + // Tier 2 slots are between 30% and 50% of the hive, depending + // on how many T3s there are. + slots[TIER_2][OPEN_SLOTS] = max(0, Ceiling(0.5*effective_total/tier_slot_multiplier) - used_tier_2_slots - used_tier_3_slots) + + return slots + +#undef TIER_3 +#undef TIER_2 +#undef OPEN_SLOTS +#undef GUARANTEED_SLOTS + +// returns if that location can be used to plant eggs +/datum/faction/proc/in_egg_plant_range(turf/T) + if(!istype(living_xeno_queen)) + return TRUE // xenos already dicked without queen. Let them plant whereever + + if(!living_xeno_queen.ovipositor) + return FALSE // ovid queen only + + return get_dist(living_xeno_queen, T) <= egg_planting_range + +/datum/faction/proc/can_build_structure(structure_name) + if(!structure_name || !faction_structures_limit[structure_name]) + return FALSE + var/total_count = 0 + if(faction_structures[structure_name]) + total_count += faction_structures[structure_name].len + if(faction_constructions[structure_name]) + total_count += faction_constructions[structure_name].len + if(total_count >= faction_structures_limit[structure_name]) + return FALSE + return TRUE + +/datum/faction/proc/has_structure(structure_name) + if(!structure_name) + return FALSE + if(faction_structures[structure_name] && faction_structures[structure_name].len) + return TRUE + return FALSE + +/datum/faction/proc/add_construction(obj/effect/alien/resin/construction/resin) + if(!resin || !resin.template) + return FALSE + var/name_ref = initial(resin.template.name) + if(!faction_constructions[name_ref]) + faction_constructions[name_ref] = list() + if(faction_constructions[name_ref].len >= faction_structures_limit[name_ref]) + return FALSE + faction_constructions[name_ref] += src + return TRUE + +/datum/faction/proc/remove_construction(obj/effect/alien/resin/construction/resin) + if(!resin || !resin.template) + return FALSE + var/name_ref = initial(resin.template.name) + faction_constructions[name_ref] -= src + return TRUE + +/datum/faction/proc/add_special_structure(obj/effect/alien/resin/special/resin) + if(!resin) + return FALSE + var/name_ref = initial(resin.name) + if(!faction_structures[name_ref]) + faction_structures[name_ref] = list() + if(faction_structures[name_ref].len >= faction_structures_limit[name_ref]) + return FALSE + faction_structures[name_ref] += resin + return TRUE + +/datum/faction/proc/remove_special_structure(obj/effect/alien/resin/special/resin) + if(!resin) + return FALSE + var/name_ref = initial(resin.name) + faction_structures[name_ref] -= resin + return TRUE + +/datum/faction/proc/has_special_structure(name_ref) + if(!name_ref || !faction_structures[name_ref] || !faction_structures[name_ref].len) + return 0 + return faction_structures[name_ref].len + +/datum/faction/proc/abandon_on_hijack() + var/area/hijacked_dropship = get_area(living_xeno_queen) + var/xenos_count = 0 + var/shipside_humans_weighted_count = 0 + for(var/name_ref in faction_structures) + for(var/obj/effect/alien/resin/special/resin in faction_structures[name_ref]) + if(get_area(resin) == hijacked_dropship) + continue + resin.hijack_delete = TRUE + faction_structures[name_ref] -= resin + qdel(resin) + for(var/mob/living/carbon/xenomorph/xeno as anything in totalMobs) + if(get_area(xeno) != hijacked_dropship && xeno.loc && is_ground_level(xeno.loc.z)) + if(isfacehugger(xeno) || islesserdrone(xeno)) + to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, you quickly find a hiding place to enter hibernation as you lose touch with the hive mind.")) + if(xeno.stomach_contents.len) + xeno.devour_timer = 0 + xeno.handle_stomach_contents() + qdel(xeno) + continue + if(xeno.hunter_data.hunted && !isqueen(xeno)) + to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, seperating you from her hive! You must defend yourself from the headhunter before you can enter hibernation...")) + xeno.set_hive_and_update(GLOB.faction_datum[FACTION_XENOMORPH_FORSAKEN]) + else + to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, you quickly find a hiding place to enter hibernation as you lose touch with the hive mind.")) + if(xeno.stomach_contents.len) + xeno.devour_timer = 0 + xeno.handle_stomach_contents() + qdel(xeno) + stored_larva++ + continue + if(xeno.tier >= 1) + xenos_count++ + for(var/i in GLOB.alive_mob_list) + var/mob/living/potential_host = i + if(!(potential_host.status_flags & XENO_HOST)) + continue + if(!is_ground_level(potential_host.z) || get_area(potential_host) == hijacked_dropship) + continue + var/obj/item/alien_embryo/A = locate() in potential_host + if(A && A.faction != src) + continue + for(var/obj/item/alien_embryo/embryo in potential_host) + embryo.faction = GLOB.faction_datum[FACTION_XENOMORPH_FORSAKEN] + potential_host.update_med_icon() + for(var/mob/living/carbon/human/current_human as anything in GLOB.alive_human_list) + if(!(isspecieshuman(current_human) || isspeciessynth(current_human))) + continue + var/datum/job/job = SSticker.role_authority.roles_for_mode[current_human.job] + if(!job) + continue + var/turf/turf = get_turf(current_human) + if(is_mainship_level(turf?.z)) + shipside_humans_weighted_count += current_human.faction.get_role_coeff(current_human.job) + hijack_burrowed_surge = TRUE + hijack_burrowed_left = max(n_ceil(shipside_humans_weighted_count * 0.5) - xenos_count, 5) + hivecore_cooldown = FALSE + xeno_message(SPAN_XENOBOLDNOTICE("The weeds have recovered! A new hive core can be built!"), 3, src) + +/datum/faction/proc/update_lesser_drone_limit() + var/countable_xeno_iterator = 0 + for(var/mob/living/carbon/xenomorph/cycled_xeno as anything in totalMobs) + if(cycled_xeno.counts_for_slots) + countable_xeno_iterator++ + + lesser_drone_limit = max(round(countable_xeno_iterator / playable_lesser_drones_max_divisor), lesser_drone_minimum) + +/datum/faction/proc/can_spawn_as_lesser_drone(mob/dead/observer/user, obj/effect/alien/resin/special/pylon/spawning_pylon) + if(jobban_isbanned(user, JOB_XENOMORPH)) // User is jobbanned + to_chat(user, SPAN_WARNING("You are banned from playing aliens and cannot spawn as a xenomorph.")) + return FALSE + + if(world.time - user.timeofdeath < JOIN_AS_LESSER_DRONE_DELAY) + var/time_left = round((user.timeofdeath + JOIN_AS_LESSER_DRONE_DELAY - world.time) / 10) + to_chat(user, SPAN_WARNING("You ghosted too recently. You cannot become a lesser drone until 30 seconds have passed ([time_left] seconds remaining).")) + return FALSE + + if(totalMobs.len <= 0) + to_chat(user, SPAN_WARNING("The hive has fallen, you can't join it!")) + return FALSE + + if(!living_xeno_queen) + to_chat(user, SPAN_WARNING("The selected hive does not have a Queen!")) + return FALSE + + if(spawning_pylon.lesser_drone_spawns < 1) + to_chat(user, SPAN_WARNING("The selected core or pylon does not have enough power for a lesser drone!")) + return FALSE + + update_lesser_drone_limit() + + var/current_lesser_drone_count = 0 + for(var/mob/mob as anything in totalMobs) + if(islesserdrone(mob)) + current_lesser_drone_count++ + + if(lesser_drone_limit <= current_lesser_drone_count) + to_chat(user, SPAN_WARNING("[name] cannot support more lesser drones! Limit: [current_lesser_drone_count]/[lesser_drone_limit]")) + return FALSE + + if(!user.client) + return FALSE + + return TRUE + +/datum/faction/proc/on_leader_death() //break alliances on queen's death + return TRUE + +/datum/faction/proc/free_respawn(client/xeno_client) + if(!faction_location || !faction_location.spawn_burrowed_larva(xeno_client.mob)) + stored_larva-- + else + stored_larva++ + faction_ui.update_burrowed_larva() + +/datum/faction/proc/respawn_on_turf(client/xeno_client, turf/spawning_turf) + var/mob/living/carbon/xenomorph/larva/new_xeno = spawn_faction_larva(spawning_turf, src) + if(isnull(new_xeno)) + return FALSE + + if(!SSticker.mode.transfer_xenomorph(xeno_client.mob, new_xeno)) + qdel(new_xeno) + return FALSE + + new_xeno.visible_message(SPAN_XENODANGER("A larva suddenly emerges from a dead husk!"), + SPAN_XENOANNOUNCE("The hive has no core! You manage to emerge from your old husk as a larva!")) + msg_admin_niche("[key_name(new_xeno)] respawned at \a [spawning_turf]. [ADMIN_JMP(spawning_turf)]") + playsound(new_xeno, 'sound/effects/xeno_newlarva.ogg', 50, 1) + if(new_xeno.client?.prefs?.toggles_flashing & FLASH_POOLSPAWN) + window_flash(new_xeno.client) + + faction_ui.update_burrowed_larva() + +/datum/faction/proc/do_buried_larva_spawn(mob/spawn_candidate) + var/spawning_area + if(faction_location) + spawning_area = faction_location + else if(living_xeno_queen) + spawning_area = living_xeno_queen + else for(var/mob/living/carbon/mob as anything in totalMobs) + if(islarva(spawn_candidate) || isxeno_builder(mob)) //next to xenos that should be in a safe spot + spawning_area = spawn_candidate + if(!spawning_area) + return FALSE + + var/list/turf_list + for(var/turf/open/open_turf in orange(3, spawning_area)) + LAZYADD(turf_list, open_turf) + + var/turf/open/spawning_turf = pick(turf_list) + + var/mob/living/carbon/xenomorph/larva/new_xenomorph = new(spawning_turf, null, src) + if(isnull(new_xenomorph)) + return FALSE + + if(!SSticker.mode.transfer_xenomorph(spawn_candidate, new_xenomorph)) + qdel(new_xenomorph) + return FALSE + new_xenomorph.visible_message(SPAN_XENODANGER("A larva suddenly burrows out of \the [spawning_turf]!"), + SPAN_XENODANGER("You burrow out of \the [spawning_turf] and awaken from your slumber. For the Hive!")) + msg_admin_niche("[key_name(new_xenomorph)] burrowed out from \a [spawning_turf]. (JMP)") + playsound(new_xenomorph, 'sound/effects/xeno_newlarva.ogg', 50, 1) + to_chat(new_xenomorph, SPAN_XENOANNOUNCE("You are a xenomorph larva awakened from slumber!")) + if(new_xenomorph.client) + if(new_xenomorph.client?.prefs?.toggles_flashing & FLASH_POOLSPAWN) + window_flash(new_xenomorph.client) + + stored_larva-- + faction_ui.update_burrowed_larva() + +///Called by /obj/item/alien_embryo when a host is bursting to determine extra larva per burst +/datum/faction/proc/increase_larva_after_burst() + var/extra_per_burst = CONFIG_GET(number/extra_larva_per_burst) + partial_larva += extra_per_burst + convert_partial_larva_to_full_larva() + +///Called after times when partial larva are added to process them to stored larva +/datum/faction/proc/convert_partial_larva_to_full_larva() + for(var/i = 1 to partial_larva) + partial_larva-- + stored_larva++ + +/datum/faction/proc/make_leader(mob/living/carbon/mob) + if(!istype(mob)) + return + + if(mob.stat == DEAD) + return + + if(faction_leader) + UnregisterSignal(faction_leader, COMSIG_PARENT_QDELETING) + + faction_leader = mob + RegisterSignal(faction_leader, COMSIG_PARENT_QDELETING, PROC_REF(handle_qdelete)) + +/datum/faction/proc/handle_qdelete(mob/living/carbon/mob) + SIGNAL_HANDLER + + if(mob == faction_leader) + faction_leader = null + +//Roles and join stuff +/datum/faction/proc/get_role_coeff(role_name) + if(coefficient_per_role[role_name]) + return coefficient_per_role[role_name] + return 1 + +/datum/faction/proc/store_objective(datum/cm_objective/O) + if(objective_memory) + objective_memory.store_objective(O) + +//FACTION INFO PANEL +/datum/faction/ui_state(mob/user) + return GLOB.not_incapacitated_state + +/datum/faction/ui_status(mob/user, datum/ui_state/state) + . = ..() + if(isobserver(user)) + return UI_INTERACTIVE + +/datum/faction/ui_data(mob/user) + . = list() + .["faction_orders"] = orders + +/datum/faction/ui_static_data(mob/user) + . = list() + .["faction_color"] = ui_color + .["faction_name"] = name + .["faction_desc"] = desc + .["actions"] = get_faction_actions() + +/datum/faction/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "FactionStatus", "[name] Статус") + ui.set_autoupdate(FALSE) + ui.open() + +/datum/faction/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + + switch(action) + if("relations") + relations_datum.tgui_interact(usr) + if("tasks") + task_interface.tgui_interact(usr) + if("clues") + if(!skillcheck(usr, SKILL_INTEL, SKILL_INTEL_TRAINED)) + to_chat(usr, SPAN_WARNING("You have no access to the [name] intel network.")) + return + objective_interface.tgui_interact(usr) + if("researchs") + if(!skillcheck(usr, SKILL_RESEARCH, SKILL_RESEARCH_TRAINED)) + to_chat(usr, SPAN_WARNING("You have no access to the [name] research network.")) + return + research_objective_interface.tgui_interact(usr) + if("status") + get_faction_info(usr) + +/datum/faction/proc/get_faction_actions(mob/user) + . = list() + . += list(list("name" = "Faction Relations", "action" = "relations")) + . += list(list("name" = "Faction Tasks", "action" = "tasks")) + . += list(list("name" = "Faction Clues", "action" = "clues")) + . += list(list("name" = "Faction Researchs", "action" = "researchs")) + . += list(list("name" = "Faction Status", "action" = "status")) + return . + +/datum/faction/proc/get_faction_info(mob/user) + var/dat = GLOB.data_core.get_manifest(FALSE, src) + if(!dat) + return FALSE + show_browser(user, dat, "Список Экипажа [name]", "manifest", "size=450x750") + return TRUE + +/datum/faction/proc/get_join_status(mob/new_player/user, dat) + dat = "
" + dat += "[user.client.auto_lang(LANGUAGE_LOBBY_ROUND_TIME)]: [DisplayTimeText(world.time, language = user.client.language)]
" + dat += "[user.client.auto_lang(LANGUAGE_LOBBY_LATE_JOIN_CHOSE)]:
" + dat += additional_join_status(user) + + if(!latejoin_enabled) + dat = "[user.client.auto_lang(LANGUAGE_LOBBY_LATE_JOIN_CLOSED)]:
" + + else if(!SSautobalancer.can_join(src)) + dat = "[user.client.auto_lang(LANGUAGE_JS_BALANCE_ISSUE)]:
" + + else + var/list/roles = roles_list[SSticker.mode.name] + for(var/i in roles) + var/datum/job/job = SSticker.role_authority.roles_by_name[i] + var/check_result = SSticker.role_authority.check_role_entry(user, job, src, TRUE) + var/active = 0 + for(var/mob/mob in GLOB.player_list) + if(mob.client && mob.job == job.title) + active++ + + if(check_result) + dat += "[job.disp_title] ([job.current_positions]): [check_result] ([user.client.auto_lang(LANGUAGE_LOBBY_LATE_JOIN_ACTIVE)]: [active])
" + else + dat += "[job.disp_title] ([job.current_positions]) ([user.client.auto_lang(LANGUAGE_LOBBY_LATE_JOIN_ACTIVE)]: [active])
" + + dat += "
" + show_browser(user, dat, user.client.auto_lang(LANGUAGE_LOBBY_LATE_JOIN), "latechoices", "size=420x700") + +/datum/faction/proc/additional_join_status(mob/new_player/user, dat = "") + return +/* + if(roles_show & FLAG_SHOW_CIC && ROLES_CIC & job.title) + dat += "
[user.client.auto_lang(LANGUAGE_LOBBY_LATE_JOIN_COM)]:
" + roles_show ^= FLAG_SHOW_CIC + + else if(roles_show & FLAG_SHOW_AUXIL_SUPPORT && ROLES_AUXIL_SUPPORT & job.title) + dat += "
[user.client.auto_lang(LANGUAGE_LOBBY_LATE_JOIN_SUP)]:
" + roles_show ^= FLAG_SHOW_AUXIL_SUPPORT + + else if(roles_show & FLAG_SHOW_MISC && ROLES_MISC & job.title) + dat += "
[user.client.auto_lang(LANGUAGE_LOBBY_LATE_JOIN_OTH)]:
" + roles_show ^= FLAG_SHOW_MISC + + else if(roles_show & FLAG_SHOW_POLICE && ROLES_POLICE & job.title) + dat += "
[user.client.auto_lang(LANGUAGE_LOBBY_LATE_JOIN_POL)]:
" + roles_show ^= FLAG_SHOW_POLICE + + else if(roles_show & FLAG_SHOW_ENGINEERING && ROLES_ENGINEERING & job.title) + dat += "
[user.client.auto_lang(LANGUAGE_LOBBY_LATE_JOIN_ENG)]:
" + roles_show ^= FLAG_SHOW_ENGINEERING + + else if(roles_show & FLAG_SHOW_REQUISITION && ROLES_REQUISITION & job.title) + dat += "
[user.client.auto_lang(LANGUAGE_LOBBY_LATE_JOIN_CAG)]:
" + roles_show ^= FLAG_SHOW_REQUISITION + + else if(roles_show & FLAG_SHOW_MEDICAL && ROLES_MEDICAL & job.title) + dat += "
[user.client.auto_lang(LANGUAGE_LOBBY_LATE_JOIN_MED)]:
" + roles_show ^= FLAG_SHOW_MEDICAL + + else if(roles_show & FLAG_SHOW_MARINES && ROLES_MARINES & job.title) + dat += "
[user.client.auto_lang(LANGUAGE_LOBBY_LATE_JOIN_MAR)]:
" + roles_show ^= FLAG_SHOW_MARINES +*/ diff --git a/code/datums/factions/freelancer.dm b/code/datums/factions/freelancer.dm new file mode 100644 index 000000000000..7d675f78e3a0 --- /dev/null +++ b/code/datums/factions/freelancer.dm @@ -0,0 +1,7 @@ +/datum/faction/freelancers + name = NAME_FACTION_FREELANCER + desc = "The term Freelancers come in various forms. Whether one is an independent entrepreneur selling services or a Freelance worker offering their labour for cash. In the business of killing and protecting, the term Freelancers are usually associated with a group of mercenaries that bare such a moniker. \ + These Freelance Mercenaries have become a small but noticeable security firm that has grown somewhat popular in the outer rim of the galaxy. Yet many sovereign nations view them as a nuisance because of the many questionable activities they've conducted. \ + None the less they're the least hated group in the galaxy as they've usually kept out of any major incidents that would reflect poorly on the Freelancers. Their alignment in the grand scheme of things usually favour those who pay them, with or without ethics." + + faction_name = FACTION_FREELANCER diff --git a/code/datums/factions/gladiator.dm b/code/datums/factions/gladiator.dm new file mode 100644 index 000000000000..ad2c023bf7b1 --- /dev/null +++ b/code/datums/factions/gladiator.dm @@ -0,0 +1,5 @@ +/datum/faction/gladiator + name = NAME_FACTION_GLADIATOR + desc = "Warning, something corrupted, this is row is not exist. Our story teller is SLEEPING, try again in some weeks." + + faction_name = FACTION_GLADIATOR diff --git a/code/datums/factions/hefa_order.dm b/code/datums/factions/hefa_order.dm new file mode 100644 index 000000000000..f6a5dbbc3fbd --- /dev/null +++ b/code/datums/factions/hefa_order.dm @@ -0,0 +1,8 @@ +/datum/faction/hefa + name = NAME_FACTION_HEFA + desc = "The Holy Order of the High-Explosive Fragmenting-Antipersonnel hand grenade (also known as the HEFA Knights, the Knights of HEFA, and or the HEFA Order), is a religious cult that is actively opposed to the United Americas and the Union of Progressive Peoples. \ + The Order's operations are loosely coordinated between two cell types, a 'temple' group, acting as a dispatch and administration center, and a 'shard' or more traditionally described splinter that actually carries out the strike. \ + It is believed that all HEFA Knight 'temple' cells operate out of the derelict Kerchner 2155 Horn Nebula mining expedition way-stations due to their proximity to supply lanes leading to the Neroid sector and other frontier sectors. The group is classified as an intergalactic terrorist organization by both the UPP, and UA. \ + HEFA Knight members or Shrapnelsworn, are devoted to the worship of the M40 High-Explosive Fragmenting-Antipersonnel hand grenade, viewing the object as a divine gift from an unknown benefactor that has influenced humanity. Order members are trained to have no regard for their personal safety. To die in battle is to die gloriously, and to solidify one's devotion to the HEFA hand grenade." + + faction_name = FACTION_HEFA diff --git a/code/datums/factions/helpers.dm b/code/datums/factions/helpers.dm deleted file mode 100644 index 38cd6dd85d58..000000000000 --- a/code/datums/factions/helpers.dm +++ /dev/null @@ -1,14 +0,0 @@ -GLOBAL_LIST_INIT_TYPED(faction_datums, /datum/faction, setup_faction_list()) - -/proc/setup_faction_list() - var/list/faction_datums_list = list() - for(var/T in typesof(/datum/faction)) - var/datum/faction/F = new T - faction_datums_list[F.faction_tag] = F - return faction_datums_list - -/proc/get_faction(faction = FACTION_MARINE) - var/datum/faction/F = GLOB.faction_datums[faction] - if(F) - return F - return GLOB.faction_datums[FACTION_NEUTRAL] diff --git a/code/datums/factions/mercenary.dm b/code/datums/factions/mercenary.dm new file mode 100644 index 000000000000..d2fb5c8e1ea0 --- /dev/null +++ b/code/datums/factions/mercenary.dm @@ -0,0 +1,5 @@ +/datum/faction/mercenary + name = NAME_FACTION_MERCENARY + desc = "Warning, potential dead story teller." + + faction_name = FACTION_MERCENARY diff --git a/code/datums/factions/pirate.dm b/code/datums/factions/pirate.dm new file mode 100644 index 000000000000..97db12664ab1 --- /dev/null +++ b/code/datums/factions/pirate.dm @@ -0,0 +1,5 @@ +/datum/faction/pirate + name = NAME_FACTION_PIRATE + desc = "Pirates, all information encrupted." + + faction_name = FACTION_PIRATE diff --git a/code/datums/factions/pizza_delivery.dm b/code/datums/factions/pizza_delivery.dm new file mode 100644 index 000000000000..24ccd82aa3c9 --- /dev/null +++ b/code/datums/factions/pizza_delivery.dm @@ -0,0 +1,7 @@ +/datum/faction/pizza + name = NAME_FACTION_PIZZA + desc = "Pizza Galaxy is the galaxy's largest pizza chain, known for its incredible variety in pizzas, and pizza related products. Customers can get anything from a plain cheese pizza, to a pizza stuffed Mr. Pizza Man mascot, with ranch cups for eyes. \ + Most Pizza Galaxy stations also have a play room for kids, called \"Pizza Party\". Pizza Galaxy is a big employer, from 16 year olds trying to pay off space college, to 40 year olds who just got discharged from the USCM for thinking that the MP was a \"red communist\". \ + The current owner and CEO of Pizza Galaxy is Mr. James Kepplinger, a prestigious businessman with a knack for bargaining." + + faction_name = FACTION_PIZZA diff --git a/code/datums/factions/ress.dm b/code/datums/factions/ress.dm new file mode 100644 index 000000000000..70d6e9a05d9d --- /dev/null +++ b/code/datums/factions/ress.dm @@ -0,0 +1,5 @@ +/datum/faction/ress + name = NAME_FACTION_RESS + desc = "No information given, please update your local data." + + faction_name = FACTION_RESS diff --git a/code/datums/factions/souto.dm b/code/datums/factions/souto.dm new file mode 100644 index 000000000000..3e007da6ee0e --- /dev/null +++ b/code/datums/factions/souto.dm @@ -0,0 +1,5 @@ +/datum/faction/souto + name = NAME_FACTION_SOUTO + desc = "Souto delivery, information encrypted due to error." + + faction_name = FACTION_SOUTO diff --git a/code/datums/factions/twe.dm b/code/datums/factions/twe.dm new file mode 100644 index 000000000000..772a7ab6011d --- /dev/null +++ b/code/datums/factions/twe.dm @@ -0,0 +1,12 @@ +/datum/faction/threewe + name = NAME_FACTION_THREEWE + desc = "Formed in 2088, the Three World Empire (3WE) was a federation of nations created by the joining of the United Kingdom and Japan, as well as allied nations including India, Indonesia, and Australia. Following the formation of this federation, the Japanese Yutani Corporation purchased and merged with the British Weyland Corporation leading to the 3WE’s status as the most significant economic force in Sol. The name, Three World Empire, is drawn from the first three worlds colonized by humanity: Earth, Mars, and Titan. \ + Supported by the technological advances of the Weyland-Yutani Corporation, the Three World Empire quickly amassed the largest and most advanced naval fleet that humanity had ever seen. These advances in Starflight allowed members of the 3WE to be the first humans to leave the cradle of Sol in search of new homes among the stars. As a result, the 3WE has colonized a plurality of all known resource-rich and naturally habitable worlds. \ + These early colonies would come to be known as the Core Systems. Due to their heavy reliance on the navy, the Three World Empire can ill afford to field a large standing army like the United Americas (UA) or the Union of Progressive Peoples (UPP). The safety of its territories outside of Sol is primarily contracted to Weyland-Yutani Private Military Contractors (PMCs) as well as the UA’s United States Colonial Marine Corps (USCMC). \ + While the USCMC is exclusively tasked with patrol and rapid response, Wey-Yu PMCs fill a wide array of roles. While the contractors are primarily hired to patrol and garrison 3WE territory, it is not uncommon to see them filling the role of peacekeepers not dissimilar to beat cops on worlds without a strong Colonial Marshal presence. For its economic influence and role in the Three World Empire’s stability and prosperity, Weyland-Yutani was granted a permanent seat in the Empire’s Parliament. \ + So far, Weyland-Yutani is the only non-governmental entity to be bestowed this honour. While all governmental parties claim that Weyland-Yutani has remained unbiased in its governance, none can deny that since being invited into Parliament, the Corporation’s economic power has increased ten-fold. In contrast, the majority of its competition have floundered under strict economic regulation and bureaucratic red tape." + + faction_name = FACTION_TWE + faction_tag = SIDE_FACTION_TWE + relations_pregen = RELATIONS_FACTION_TWE + faction_iff_tag_type = /obj/item/faction_tag/twe diff --git a/code/datums/factions/royalmarinescommando.dm b/code/datums/factions/twe/commando.dm similarity index 90% rename from code/datums/factions/royalmarinescommando.dm rename to code/datums/factions/twe/commando.dm index 2fab0348bcfa..c2da3242e072 100644 --- a/code/datums/factions/royalmarinescommando.dm +++ b/code/datums/factions/twe/commando.dm @@ -1,15 +1,17 @@ -/datum/faction/royal_marines_commando - name = "Royal Marines Commando" +/datum/faction/twe/royal_commando + name = NAME_FACTION_TWE + desc = "No information given, please update your local data." + faction_tag = FACTION_TWE -/datum/faction/royal_marines_commando/modify_hud_holder(image/holder, mob/living/carbon/human/H) +/datum/faction/twe/royal_commando/modify_hud_holder(image/holder, mob/living/carbon/human/human) var/hud_icon_state - var/obj/item/card/id/dogtag/ID = H.get_idcard() + var/obj/item/card/id/id_card = human.get_idcard() var/_role - if(H.mind) - _role = H.job - else if(ID) - _role = ID.rank + if(human.mind) + _role = human.job + else if(id_card) + _role = id_card.rank switch(_role) if(JOB_TWE_RMC_LIEUTENANT) hud_icon_state = "lieutenant" @@ -26,7 +28,7 @@ if(hud_icon_state) holder.overlays += image('icons/mob/hud/marine_hud.dmi', H, "rmc_[hud_icon_state]") -/datum/faction/royal_marines_commando/get_antag_guns_snowflake_equipment() +/datum/faction/twe/royal_commando/get_antag_guns_snowflake_equipment() return list( list("PRIMARY FIREARMS", 0, null, null, null), list("F903A1 Rifle", 20, /obj/item/weapon/gun/rifle/rmc_f90, null, VENDOR_ITEM_REGULAR), @@ -65,7 +67,7 @@ list("L5 bayonet", 3, /obj/item/attachable/bayonet/rmc, null, VENDOR_ITEM_REGULAR), ) -/datum/faction/royal_marines_commando/get_antag_guns_sorted_equipment() +/datum/faction/twe/royal_commando/get_antag_guns_sorted_equipment() return list( list("PRIMARY FIREARMS", -1, null, null), list("F903A1 Rifle", 20, /obj/item/weapon/gun/rifle/rmc_f90, null, VENDOR_ITEM_REGULAR), diff --git a/code/datums/factions/upp.dm b/code/datums/factions/upp.dm index 90b04765cf85..382e9e9e8cc1 100644 --- a/code/datums/factions/upp.dm +++ b/code/datums/factions/upp.dm @@ -1,15 +1,76 @@ /datum/faction/upp - name = "Union of Progressive Peoples" - faction_tag = FACTION_UPP + name = NAME_FACTION_UPP + desc = "A wide-reaching and powerful socialist authoritarian state. The UPP acts as the political and ideological rival of the United States and the UA. Though they have a relatively large presence on Earth (particularly in the Eastern sphere), the UPP is also largely active in colonizing the frontier. \ + While the United Americas have never engaged the UPP in an official war, hostilities broke out between a USCM Task Force and a UPP Expeditionary Force during Operation Canton in mid 2165. The engagement ended in a ceasefire. The reason was fears that the conflict would spread to the more densely populated Sol System. \ + The incident greatly enraged many senior officials in the UPP, who viewed the battle of Canton as an attempt by the United States to annex UPP sovereign territory. “Such an insult (referring to Canton) can only be repaid in American blood. Mark my words, this will happen”, Kolonel Ganbaatar of the UPP Armed Forces was quoted saying in May 2168." -/datum/faction/upp/modify_hud_holder(image/holder, mob/living/carbon/human/H) + faction_name = FACTION_UPP + faction_tag = SIDE_FACTION_UPP + relations_pregen = RELATIONS_FACTION_UPP + faction_iff_tag_type = /obj/item/faction_tag/upp + + role_mappings = list( + MODE_NAME_EXTENDED = list(), + MODE_NAME_DISTRESS_SIGNAL = list(), + MODE_NAME_FACTION_CLASH = list(), + MODE_NAME_HUMAN_WARS = list( + /datum/job/civilian/liaison/combat_reporter/upp = JOB_UPP_CORPORATE_LIAISON + ), + MODE_NAME_CRASH = list(), + MODE_NAME_WISKEY_OUTPOST = list(), + MODE_NAME_HUNTER_GAMES = list(), + MODE_NAME_HIVE_WARS = list(), + MODE_NAME_INFECTION = list() + ) + roles_list = list( + MODE_NAME_EXTENDED = list(), + MODE_NAME_DISTRESS_SIGNAL = list(), + MODE_NAME_FACTION_CLASH = UPP_JOB_LIST, + MODE_NAME_HUMAN_WARS = UPP_JOB_LIST, + MODE_NAME_CRASH = list(), + MODE_NAME_WISKEY_OUTPOST = list(), + MODE_NAME_HUNTER_GAMES = list(), + MODE_NAME_HIVE_WARS = list(), + MODE_NAME_INFECTION = list() + ) + coefficient_per_role = list( + JOB_UPP_KOL_OFFICER = 2.5, + JOB_UPP_MAY_OFFICER = 1.75, + JOB_UPP_SRLT_OFFICER = 1.5, + JOB_UPP_LT_OFFICER = 1.25, + JOB_UPP_INTEL = 1, + JOB_UPP_CREWMAN = 4, + JOB_UPP_POLICE = 0.5, + JOB_UPP_CORPORATE_LIAISON = 0.25, + JOB_UPP_COMBAT_SYNTH = 6, + JOB_UPP_LT_DOKTOR = 2, + JOB_UPP_LEADER = 1.75, + JOB_UPP_CONSCRIPT = 1.5, + JOB_UPP_SPECIALIST = 3, + JOB_UPP_MEDIC = 2.25, + JOB_UPP_ENGI = 2, + JOB_UPP = 1.25 + ) + weight_act = list( + MODE_NAME_EXTENDED = FALSE, + MODE_NAME_DISTRESS_SIGNAL = FALSE, + MODE_NAME_FACTION_CLASH = TRUE, + MODE_NAME_HUMAN_WARS = TRUE, + MODE_NAME_CRASH = FALSE, + MODE_NAME_WISKEY_OUTPOST = FALSE, + MODE_NAME_HUNTER_GAMES = FALSE, + MODE_NAME_HIVE_WARS = FALSE, + MODE_NAME_INFECTION = FALSE + ) + +/datum/faction/upp/modify_hud_holder(image/holder, mob/living/carbon/human/human) var/hud_icon_state - var/obj/item/card/id/ID = H.get_idcard() + var/obj/item/card/id/id_card = human.get_idcard() var/_role - if(H.mind) - _role = H.job - else if(ID) - _role = ID.rank + if(human.mind) + _role = human.job + else if(id_card) + _role = id_card.rank switch(_role) if(JOB_UPP_MEDIC) hud_icon_state = "med" diff --git a/code/datums/factions/uscm.dm b/code/datums/factions/uscm.dm index f7c49321f305..aa1a2b996b9e 100644 --- a/code/datums/factions/uscm.dm +++ b/code/datums/factions/uscm.dm @@ -1,6 +1,13 @@ /datum/faction/uscm - name = "United States Colonial Marines" - faction_tag = FACTION_MARINE + name = NAME_FACTION_USCM + desc = "The USCM is divided into three overarching branches, called the Marine Space Forces: Sol, overseeing the core colonies and earth, the largest of the three; Eridani, operating among the Chinese and American colonised arms; Herculis, going through the Anglo-Japanese arms and the outer fringes. Each branch operates independently, but all are responsible for protecting American space territory and working together in that goal. \ + There is also a fourth group, the Reserves, which is stationed mostly on earth and serves to reinforce the other branches. They have not seen any active use yet, but who knows what the future holds. Marine Space Force, Herculis, Chinook 91 GSO station is a military space station in geosynchronous orbit around the colony world Georgia 525 (70 Ophiuchi A V), and is the headquarters for the Herculis branch of the USCM. Under its control are the 4th Colonial Marine Division, 4th Colonial Marine Brigade, 4th Aerospace Wing, and 1st Colonial Support Group of the USCM. \ + Chinook 91 reports directly to O'Neill station in Earth-Lunar space. The O'Neill station is the main communication base between the three branches. Supervising orders from the homeworld, controlling the First Fleet and rare joint operations. It is an extremely vital station and well guarded by the First Fleet in all its might. Tithonis Mountain on Bernice 378 is another large garrison of USCM troops, such as the 1st Colonial Support Group, and there are more minor garrisons along the Anglo-Japanese arm. The 2nd Company of the 2nd Battalion of the 4th Brigade, the Falling Falcons, and the USS Almayer are stationed in Herculis." + + faction_name = FACTION_USCM + faction_tag = SIDE_FACTION_USCM + relations_pregen = RELATIONS_FACTION_USCM + faction_iff_tag_type = /obj/item/faction_tag/uscm /datum/faction/uscm/modify_hud_holder(image/holder, mob/living/carbon/human/current_human) var/datum/squad/squad = current_human.assigned_squad diff --git a/code/datums/factions/uscm/cmb.dm b/code/datums/factions/uscm/cmb.dm new file mode 100644 index 000000000000..045dc8799200 --- /dev/null +++ b/code/datums/factions/uscm/cmb.dm @@ -0,0 +1,4 @@ +/datum/faction/uscm/cmb + name = NAME_FACTION_CMB + faction_name = FACTION_CMB + faction_iff_tag_type = /obj/item/faction_tag/uscm/cmb diff --git a/code/datums/factions/uscm/marine.dm b/code/datums/factions/uscm/marine.dm new file mode 100644 index 000000000000..0985e2651d2e --- /dev/null +++ b/code/datums/factions/uscm/marine.dm @@ -0,0 +1,117 @@ +/datum/faction/uscm/marine + name = NAME_FACTION_MARINE + faction_name = FACTION_MARINE + faction_iff_tag_type = /obj/item/faction_tag/uscm/marine + objectives = list("objective" = 40, "close" = 30, "medium" = 25, "far" = 10, "science" = 20) // TODO: Other way??? + objectives_active = TRUE + + role_mappings = list( + MODE_NAME_EXTENDED = list(), + MODE_NAME_DISTRESS_SIGNAL = list(), + MODE_NAME_FACTION_CLASH = list(), + MODE_NAME_HUMAN_WARS = list( + /datum/job/civilian/liaison/combat_reporter/cm = JOB_CORPORATE_LIAISON + ), + MODE_NAME_CRASH = list( + /datum/job/command/commander/crash = JOB_CO, + /datum/job/civilian/synthetic/crash = JOB_SYNTH, + /datum/job/logistics/engineering/crash = JOB_CHIEF_ENGINEER, + /datum/job/civilian/professor/crash = JOB_CMO, + /datum/job/uscm/squad/leader/crash = JOB_SQUAD_LEADER, + /datum/job/uscm/squad/specialist/crash = JOB_SQUAD_SPECIALIST, + /datum/job/uscm/squad/smartgunner/crash = JOB_SQUAD_SMARTGUN, + /datum/job/uscm/squad/medic/crash = JOB_SQUAD_MEDIC, + /datum/job/uscm/squad/engineer/crash = JOB_SQUAD_ENGI, + /datum/job/uscm/squad/standard/crash = JOB_SQUAD_MARINE + ), + MODE_NAME_WISKEY_OUTPOST = list( + /datum/job/command/commander/whiskey = JOB_CO, + /datum/job/command/executive/whiskey = JOB_XO, + /datum/job/civilian/synthetic/whiskey = JOB_SYNTH, + /datum/job/command/warrant/whiskey = JOB_CHIEF_POLICE, + /datum/job/command/bridge/whiskey = JOB_SO, + /datum/job/command/tank_crew/whiskey = JOB_CREWMAN, + /datum/job/command/police/whiskey = JOB_POLICE, + /datum/job/command/pilot/whiskey = JOB_PILOT, + /datum/job/logistics/requisition/whiskey = JOB_CHIEF_REQUISITION, + /datum/job/civilian/professor/whiskey = JOB_CMO, + /datum/job/civilian/doctor/whiskey = JOB_DOCTOR, + /datum/job/civilian/researcher/whiskey = JOB_RESEARCHER, + /datum/job/logistics/engineering/whiskey = JOB_CHIEF_ENGINEER, + /datum/job/logistics/otech/maint/whiskey = JOB_MAINT_TECH, + /datum/job/logistics/cargo/whiskey = JOB_CARGO_TECH, + /datum/job/uscm/squad/leader/whiskey = JOB_SQUAD_LEADER, + /datum/job/uscm/squad/specialist/whiskey = JOB_SQUAD_SPECIALIST, + /datum/job/uscm/squad/smartgunner/whiskey = JOB_SQUAD_SMARTGUN, + /datum/job/uscm/squad/medic/whiskey = JOB_SQUAD_MEDIC, + /datum/job/uscm/squad/engineer/whiskey = JOB_SQUAD_ENGI, + /datum/job/uscm/squad/standard/whiskey = JOB_SQUAD_MARINE + ), + MODE_NAME_HUNTER_GAMES = list(), + MODE_NAME_HIVE_WARS = list(), + MODE_NAME_INFECTION = list() + ) + roles_list = list( + MODE_NAME_EXTENDED = ROLES_REGULAR_USCM, + MODE_NAME_DISTRESS_SIGNAL = ROLES_REGULAR_USCM, + MODE_NAME_FACTION_CLASH = ROLES_REGULAR_USCM, + MODE_NAME_HUMAN_WARS = ROLES_HVH_USCM, + MODE_NAME_CRASH = ROLES_CRASH_USCM, + MODE_NAME_WISKEY_OUTPOST = ROLES_WO_USCM, + MODE_NAME_HUNTER_GAMES = list(), + MODE_NAME_HIVE_WARS = list(), + MODE_NAME_INFECTION = ROLES_REGULAR_USCM + ) + coefficient_per_role = list( + JOB_CO = 2, + JOB_XO = 1.5, + JOB_SO = 1, + JOB_INTEL = 0.5, + JOB_PILOT = 0.25, + JOB_DROPSHIP_CREW_CHIEF = 0.75, + JOB_CREWMAN = 2.5, + JOB_POLICE = 0.25, + JOB_CORPORATE_LIAISON = 0.25, + JOB_CHIEF_REQUISITION = 0.5, + JOB_CHIEF_ENGINEER = 1, + JOB_CMO = 1, + JOB_CHIEF_POLICE = 0.75, + JOB_SEA = 0.25, + JOB_SYNTH = 4, + JOB_WARDEN = 0.75, + JOB_ORDNANCE_TECH = 1.5, + JOB_MAINT_TECH = 0.75, + JOB_WORKING_JOE = 1, + JOB_MESS_SERGEANT = 0.25, + JOB_CARGO_TECH = 0.5, + JOB_RESEARCHER = 1, + JOB_DOCTOR = 1.25, + JOB_NURSE = 0.75, + JOB_SQUAD_LEADER = 1.5, + JOB_SQUAD_RTO = 1, + JOB_SQUAD_SPECIALIST = 2, + JOB_SQUAD_SMARTGUN = 1.75, + JOB_SQUAD_MEDIC = 1.5, + JOB_SQUAD_ENGI = 1.25, + JOB_SQUAD_MARINE = 1 + ) + weight_act = list( + MODE_NAME_EXTENDED = TRUE, + MODE_NAME_DISTRESS_SIGNAL = TRUE, + MODE_NAME_FACTION_CLASH = TRUE, + MODE_NAME_HUMAN_WARS = TRUE, + MODE_NAME_CRASH = TRUE, + MODE_NAME_WISKEY_OUTPOST = TRUE, + MODE_NAME_HUNTER_GAMES = FALSE, + MODE_NAME_HIVE_WARS = FALSE, + MODE_NAME_INFECTION = TRUE + ) + +/datum/faction/uscm/marine/additional_join_status(mob/new_player/user, dat = "") + if(SSevacuation) + switch(SSevacuation.evac_status) + if(EVACUATION_STATUS_INITIATING) + dat += "[replacetext(user.client.auto_lang(LANGUAGE_LOBBY_EVAC_STARTED), "###MAIN_SHIP###", "[MAIN_SHIP_NAME]")]
" + if(EVACUATION_STATUS_COMPLETE) + dat += "[replacetext(user.client.auto_lang(LANGUAGE_LOBBY_EVAC_FINISHED), "###MAIN_SHIP###", "[MAIN_SHIP_NAME]")]
" + . = ..() diff --git a/code/datums/factions/uscm/marsoc.dm b/code/datums/factions/uscm/marsoc.dm new file mode 100644 index 000000000000..17402f250447 --- /dev/null +++ b/code/datums/factions/uscm/marsoc.dm @@ -0,0 +1,6 @@ +/datum/faction/uscm/marsoc + name = NAME_FACTION_MARSOC + desc = "Decryption required" + + faction_name = FACTION_MARSOC + faction_iff_tag_type = /obj/item/faction_tag/uscm/marsoc diff --git a/code/datums/factions/wy.dm b/code/datums/factions/wy.dm new file mode 100644 index 000000000000..7cb8c13c2b44 --- /dev/null +++ b/code/datums/factions/wy.dm @@ -0,0 +1,34 @@ +/datum/faction/wy + name = NAME_FACTION_WY + desc = "Weyland Yutani also known as \"the Company\", has a wide range of business. This includes dealing with high-tech armaments, synthetic humanoids, spaceships, computer parts, terraforming equipment, and household appliances. They also offers shipping and receiving services, among other ventures. Following an aggressive expansion into terraforming new colonial world, Weyland-Yutani secured rights and privileges from the United States government. One of those privileges is the close relationship with the USCM. \ + the Colonial Marines use corporate-supplied equipment in exchange for protecting and monitoring border colonies. This, however, does not mean that the interests of the company are aligned with the USCM. It is more accurate to say that Weyland-Yutani considers the USCM a valuable, but still disposable, asset. This has led to an increasing amount of hostility between the two factions. Weyland-Yutani has enough wealth and influence to hire private military contractors. These are highly trained mercenaries, generally ex-military veterans. \ + They do black site protection detail, undertaking dangerous assignments, and otherwise supply Weyland-Yutani with firepower in the absence of the USCM. They are compensated well for their services, and demand is always there. Rumors speak of even more well-equipped and well-selected military units within Weyland-Yutani's employment, but that is not officially verified. The Company has refused to comment on it." + + faction_name = FACTION_WY + faction_tag = SIDE_FACTION_WY + relations_pregen = RELATIONS_FACTION_WY + faction_iff_tag_type = /obj/item/faction_tag/wy + +/datum/faction/pmc/modify_hud_holder(image/holder, mob/living/carbon/human/human) + var/hud_icon_state + var/obj/item/card/id/id_card = human.get_idcard() + var/_role + if(human.mind) + _role = human.job + else if(id_card) + _role = id_card.rank + switch(_role) + if(JOB_PMC_DIRECTOR) + hud_icon_state = "sd" + if(JOB_PMC_LEADER, JOB_PMC_LEAD_INVEST) + hud_icon_state = "ld" + if(JOB_PMC_DOCTOR) + hud_icon_state = "td" + if(JOB_PMC_ENGINEER) + hud_icon_state = "ct" + if(JOB_PMC_MEDIC, JOB_PMC_INVESTIGATOR) + hud_icon_state = "md" + if(JOB_PMC_SYNTH) + hud_icon_state = "syn" + if(hud_icon_state) + holder.overlays += image('icons/mob/hud/marine_hud.dmi', H, "pmc_[hud_icon_state]") diff --git a/code/datums/factions/wy/deathsquad.dm b/code/datums/factions/wy/deathsquad.dm new file mode 100644 index 000000000000..4780f9015bf6 --- /dev/null +++ b/code/datums/factions/wy/deathsquad.dm @@ -0,0 +1,6 @@ +/datum/faction/wy/deathsquad + name = NAME_FACTION_WY_DEATHSQUAD + desc = "Decryption required" + + faction_name = FACTION_WY_DEATHSQUAD + faction_iff_tag_type = /obj/item/faction_tag/wy/death_sqaud diff --git a/code/datums/factions/pmc.dm b/code/datums/factions/wy/pmc.dm similarity index 72% rename from code/datums/factions/pmc.dm rename to code/datums/factions/wy/pmc.dm index c5b319a13c7b..bedb20496a81 100644 --- a/code/datums/factions/pmc.dm +++ b/code/datums/factions/wy/pmc.dm @@ -1,32 +1,11 @@ -/datum/faction/pmc - name = "Private Military Company" - faction_tag = FACTION_PMC +/datum/faction/wy/pmc + name = NAME_FACTION_PMC + desc = "Weyland-Yutani PMCs are military personnel owned and operated by the company. They are equipped with advanced modern military equipment and weaponry akin to the USCM and similar national militaries. The tasks of the PMCs are never limited to one specific job and they are often deployed on 'shadow missions'. Their deployment is often kept secret from the USCM to avoid conflict with the anti-corporate officers in the USCM. The recruitment process largely consists of those who have personal contacts with higher Weyland-Yutani employees, or those who have caught the eye of the superiors and been hand-picked. The superiors value abilities in the field and willingness to obey company directives, for a large sum of money, despite of their moral beliefs. Following the defeat of the Dust Raiders and the withdrawal of the United Americas of the Neroid sector, a group of employees became skilled mercenaries. They are part of Weyland-Yutani's Task Force Oberon that was stationed aboard the USCSS Royce, a powerful Weyland-Yutani cruiser that patrols the outer edges of the Neroid sector. Under the directive of Weyland-Yutani's board member Johan Almric, they act as private security for company science teams. The USCSS Royce contains a crew of roughly two hundred PMCs, and one hundred scientists and support personnel. Rumors say that a Weyland-Yutani Special Task Force known as \"Royal\" of the USCSS Lunalorne are part of a different specialization, designed to capture anomalies associated with alien and supernatural life." -/datum/faction/pmc/modify_hud_holder(image/holder, mob/living/carbon/human/H) - var/hud_icon_state - var/obj/item/card/id/ID = H.get_idcard() - var/_role - if(H.mind) - _role = H.job - else if(ID) - _role = ID.rank - switch(_role) - if(JOB_PMC_DIRECTOR) - hud_icon_state = "sd" - if(JOB_PMC_LEADER, JOB_PMC_LEAD_INVEST) - hud_icon_state = "ld" - if(JOB_PMC_DOCTOR) - hud_icon_state = "td" - if(JOB_PMC_ENGINEER) - hud_icon_state = "ct" - if(JOB_PMC_MEDIC, JOB_PMC_INVESTIGATOR) - hud_icon_state = "md" - if(JOB_PMC_SYNTH) - hud_icon_state = "syn" - if(hud_icon_state) - holder.overlays += image('icons/mob/hud/marine_hud.dmi', H, "pmc_[hud_icon_state]") + faction_name = FACTION_PMC + faction_iff_tag_type = /obj/item/faction_tag/wy/pmc -/datum/faction/pmc/get_antag_guns_snowflake_equipment() +/datum/faction/wy/pmc/get_antag_guns_snowflake_equipment() return list( list("PRIMARY FIREARMS", 0, null, null, null), list("M41A/2 Pulse Rifle", 30, /obj/item/weapon/gun/rifle/m41a/elite, null, VENDOR_ITEM_REGULAR), @@ -66,7 +45,7 @@ list("Smoke Grenade", 7, /obj/item/explosive/grenade/smokebomb, null, VENDOR_ITEM_REGULAR) ) -/datum/faction/pmc/get_antag_guns_sorted_equipment() +/datum/faction/wy/pmc/get_antag_guns_sorted_equipment() return list( list("PRIMARY FIREARMS", 0, null, null, null), list("M41A/2 Pulse Rifle", 30, /obj/item/weapon/gun/rifle/m41a/elite, null, VENDOR_ITEM_REGULAR), diff --git a/code/datums/factions/xeno.dm b/code/datums/factions/xeno.dm new file mode 100644 index 000000000000..3c768e380343 --- /dev/null +++ b/code/datums/factions/xeno.dm @@ -0,0 +1,390 @@ +/datum/faction/xenomorph + name = NAME_FACTION_XENOMORPH + desc = "Xenomorph hive among the all other hives." + + faction_name = FACTION_XENOMORPH + faction_tag = SIDE_FACTION_XENOMORPH + organ_faction_iff_tag_type = /obj/item/faction_tag/organ/xenomorph + relations_pregen = RELATIONS_FACTION_XENOMORPH + + var/hive_module_type = /datum/faction_module/hive + +/datum/faction/xenomorph/faction_is_ally(datum/faction/faction) + if(!living_xeno_queen) + return FALSE + . = ..() + +/datum/faction/xenomorph/New() + . = ..() + + modules["hive"] = new hive_module_type(src) + mutators = new(src) + mark_ui = new(src) + faction_ui = new(src) + +/datum/faction/xenomorph/can_delay_round_end(mob/living/carbon/carbon) + if(!faction_is_ally(GLOB.faction_datum[FACTION_MARINE])) + return TRUE + return FALSE + +// Adds a xeno to this hive +/datum/faction/xenomorph/add_mob(mob/living/carbon/xenomorph/xenomorph) + if(!xenomorph || !istype(xenomorph)) + return + + // If the xeno is part of another hive, they should be removed from that one first + if(xenomorph.faction && xenomorph.faction != src) + xenomorph.faction.remove_mob(xenomorph, TRUE) + + // Already in the hive + if(xenomorph in totalMobs) + return + + // Can only have one queen. + if(isqueen(xenomorph)) + if(!living_xeno_queen && !xenomorph.statistic_exempt) // Don't consider xenos in admin level + set_living_xeno_queen(xenomorph) + + xenomorph.faction = src + + if(xenomorph.hud_list) + xenomorph.hud_update() + + if(!xenomorph.statistic_exempt) + totalMobs += xenomorph + if(xenomorph.tier == 2) + tier_2_xenos += xenomorph + else if(xenomorph.tier == 3) + tier_3_xenos += xenomorph + + // Xenos are a fuckfest of cross-dependencies of different datums that are initialized at different times + // So don't even bother trying updating UI here without large refactors + +// Removes the xeno from the hive +/datum/faction/xenomorph/remove_mob(mob/living/carbon/xenomorph/xenomorph, hard = FALSE, light_mode = FALSE) + if(!xenomorph || !istype(xenomorph)) + return + + // Make sure the xeno was in the hive in the first place + if(!(xenomorph in totalMobs)) + return + + if(isqueen(xenomorph)) + if(living_xeno_queen == xenomorph) + var/mob/living/carbon/xenomorph/queen/next_queen + for(var/mob/living/carbon/xenomorph/queen/Q in totalMobs) + if(!Q.statistic_exempt) + continue + next_queen = Q + break + + set_living_xeno_queen(next_queen) // either null or a queen + + // We allow "soft" removals from the hive (the xeno still retains information about the hive) + // This is so that xenos can add themselves back to the hive if they should die or otherwise go "on leave" from the hive + if(hard) + xenomorph.faction = null + + totalMobs -= xenomorph + if(xenomorph.tier == 2) + tier_2_xenos -= xenomorph + else if(xenomorph.tier == 3) + tier_3_xenos -= xenomorph + + if(!light_mode) + faction_ui.update_xeno_counts() + faction_ui.xeno_removed(xenomorph) + +/datum/faction/xenomorph/get_faction_info(mob/user) + if(!user || !faction_ui) + return + + if(!faction_ui.data_initialized) + faction_ui.update_all_data() + + faction_ui.tgui_interact(user) + return TRUE + +/datum/faction/xenomorph/get_join_status(mob/new_player/user, dat) + if(SSticker.current_state != GAME_STATE_PLAYING || !SSticker.mode) + to_chat(user, SPAN_WARNING(user.client.auto_lang(LANGUAGE_LOBBY_ROUND_NO_JOIN))) + return + + if(alert(user, user.client.auto_lang(LANGUAGE_LOBBY_JOIN_XENOMORPH), user.client.auto_lang(LANGUAGE_CONFIRM), user.client.auto_lang(LANGUAGE_YES), user.client.auto_lang(LANGUAGE_NO)) == user.client.auto_lang(LANGUAGE_YES)) + if(SSticker.mode.check_xeno_late_join(user)) + var/mob/new_xeno = SSticker.mode.attempt_to_join_as_xeno(user, 0) + if(new_xeno && !istype(new_xeno, /mob/living/carbon/xenomorph/larva)) + SSticker.mode.transfer_xenomorph(user, new_xeno) + user.close_spawn_windows() + + +//LANDMARKS +/datum/xeno_mark_define + var/name = "xeno_declare" + var/icon_state = "empty" + var/desc = "Xenos make psychic markers with this meaning as positional lasting communication to eachother" + +/datum/xeno_mark_define/fortify + name = "Fortify" + desc = "Fortify this area!" + icon_state = "fortify" + +/datum/xeno_mark_define/weeds + name = "Need Weeds" + desc = "Need weeds here!" + icon_state = "weed" + +/datum/xeno_mark_define/nest + name = "Nest" + desc = "Nest enemies here!" + icon_state = "nest" + +/datum/xeno_mark_define/hosts + name = "Hosts" + desc = "Hosts here!" + icon_state = "hosts" + +/datum/xeno_mark_define/aide + name = "Aide" + desc = "Aide here!" + icon_state = "aide" + +/datum/xeno_mark_define/defend + name = "Defend" + desc = "Defend the hive here!" + icon_state = "defend" + +/datum/xeno_mark_define/danger + name = "Danger Warning" + desc = "Caution, danger here!" + icon_state = "danger" + +/datum/xeno_mark_define/rally + name = "Rally" + desc = "Group up here!" + icon_state = "rally" + +/datum/xeno_mark_define/hold + name = "Hold" + desc = "Hold this area!" + icon_state = "hold" + +/datum/xeno_mark_define/ambush + name = "Ambush" + desc = "Ambush the enemy here!" + icon_state = "ambush" + +/datum/xeno_mark_define/attack + name = "Attack" + desc = "Attack the enemy here!" + icon_state = "attack" + + +//HIVE STATUS +/datum/hive_status_ui + var/name = "Hive Status" + + // Data to pass when rendering the UI (not static) + var/total_xenos + var/list/xeno_counts + var/list/tier_slots + var/list/xeno_vitals + var/list/xeno_keys + var/list/xeno_info + var/faction_location + var/burrowed_larva + var/evolution_level + + var/data_initialized = FALSE + + var/datum/faction/assoc_hive = null + +/datum/hive_status_ui/New(datum/faction/faction) + assoc_hive = faction + update_all_data() + START_PROCESSING(SShive_status, src) + +/datum/hive_status_ui/process() + update_xeno_vitals() + update_xeno_info(FALSE) + SStgui.update_uis(src) + +// Updates the list tracking how many xenos there are in each tier, and how many there are in total +/datum/hive_status_ui/proc/update_xeno_counts(send_update = TRUE) + xeno_counts = assoc_hive.get_xeno_counts() + + total_xenos = 0 + for(var/counts in xeno_counts) + for(var/caste in counts) + total_xenos += counts[caste] + + if(send_update) + SStgui.update_uis(src) + + xeno_counts[1] -= "Queen" // don't show queen in the amount of xenos + + // Also update the amount of T2/T3 slots + tier_slots = assoc_hive.get_tier_slots() + +// Updates the hive location using the area name of the defined hive location turf +/datum/hive_status_ui/proc/update_faction_location(send_update = TRUE) + if(!assoc_hive.faction_location) + return + + faction_location = strip_improper(get_area_name(assoc_hive.faction_location)) + + if(send_update) + SStgui.update_uis(src) + +// Updates the sorted list of all xenos that we use as a key for all other information +/datum/hive_status_ui/proc/update_xeno_keys(send_update = TRUE) + xeno_keys = assoc_hive.get_xeno_keys() + + if(send_update) + SStgui.update_uis(src) + +// Mildly related to the above, but only for when xenos are removed from the hive +// If a xeno dies, we don't have to regenerate all xeno info and sort it again, just remove them from the data list +/datum/hive_status_ui/proc/xeno_removed(mob/living/carbon/xenomorph/xenomorph) + if(!xeno_keys) + return + + for(var/index in 1 to length(xeno_keys)) + var/list/info = xeno_keys[index] + if(info["nicknumber"] == xenomorph.nicknumber) + + // tried Remove(), didn't work. *shrug* + xeno_keys[index] = null + xeno_keys -= null + return + + SStgui.update_uis(src) + +// Updates the list of xeno names, strains and references +/datum/hive_status_ui/proc/update_xeno_info(send_update = TRUE) + xeno_info = assoc_hive.get_xeno_info() + + if(send_update) + SStgui.update_uis(src) + +// Updates vital information about xenos such as health and location. Only info that should be updated regularly +/datum/hive_status_ui/proc/update_xeno_vitals() + xeno_vitals = assoc_hive.get_xeno_vitals() + +// Updates how many buried larva there are +/datum/hive_status_ui/proc/update_burrowed_larva(send_update = TRUE) + burrowed_larva = assoc_hive.stored_larva + if(SSxevolution) + evolution_level = SSxevolution.get_evolution_boost_power(assoc_hive) + else + evolution_level = 1 + + if(send_update) + SStgui.update_uis(src) + +// Updates all data except pooled larva +/datum/hive_status_ui/proc/update_all_xeno_data(send_update = TRUE) + update_xeno_counts(FALSE) + update_xeno_vitals() + update_xeno_keys(FALSE) + update_xeno_info(FALSE) + + if(send_update) + SStgui.update_uis(src) + +// Updates all data, including pooled larva +/datum/hive_status_ui/proc/update_all_data() + data_initialized = TRUE + update_all_xeno_data(FALSE) + update_burrowed_larva(FALSE) + SStgui.update_uis(src) + +/datum/hive_status_ui/ui_state(mob/user) + return GLOB.hive_state[assoc_hive.faction_name] + +/datum/hive_status_ui/ui_status(mob/user, datum/ui_state/state) + . = ..() + if(isobserver(user)) + return UI_INTERACTIVE + +/datum/hive_status_ui/ui_data(mob/user) + . = list() + .["total_xenos"] = total_xenos + .["xeno_counts"] = xeno_counts + .["tier_slots"] = tier_slots + .["xeno_keys"] = xeno_keys + .["xeno_info"] = xeno_info + .["xeno_vitals"] = xeno_vitals + .["queen_location"] = get_area_name(assoc_hive.living_xeno_queen) + .["faction_location"] = faction_location + .["burrowed_larva"] = burrowed_larva + .["evolution_level"] = evolution_level + + var/mob/living/carbon/xenomorph/queen/Q = user + .["is_in_ovi"] = istype(Q) && Q.ovipositor + +/datum/hive_status_ui/ui_static_data(mob/user) + . = list() + .["user_ref"] = REF(user) + .["hive_color"] = assoc_hive.ui_color + .["hive_name"] = assoc_hive.name + +/datum/hive_status_ui/tgui_interact(mob/user, datum/tgui/ui) + if(!assoc_hive) + return + + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "HiveStatus", "[assoc_hive.name] Status") + ui.set_autoupdate(FALSE) + ui.open() + +/datum/hive_status_ui/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + + switch(action) + if("give_plasma") + var/mob/living/carbon/xenomorph/target_xenomorph = locate(params["target_ref"]) in GLOB.living_xeno_list + var/mob/living/carbon/xenomorph/xenomorph = ui.user + + if(QDELETED(target_xenomorph) || target_xenomorph.stat == DEAD || target_xenomorph.statistic_exempt) + return + + if(xenomorph.stat == DEAD) + return + + var/datum/action/xeno_action/A = get_xeno_action_by_type(xenomorph, /datum/action/xeno_action/activable/queen_give_plasma) + A?.use_ability_wrapper(target_xenomorph) + + if("heal") + var/mob/living/carbon/xenomorph/target_xenomorph = locate(params["target_ref"]) in GLOB.living_xeno_list + var/mob/living/carbon/xenomorph/xenomorph = ui.user + + if(QDELETED(target_xenomorph) || target_xenomorph.stat == DEAD || target_xenomorph.statistic_exempt) + return + + if(xenomorph.stat == DEAD) + return + + var/datum/action/xeno_action/A = get_xeno_action_by_type(xenomorph, /datum/action/xeno_action/activable/queen_heal) + A?.use_ability_wrapper(target_xenomorph, TRUE) + + if("overwatch") + var/mob/living/carbon/xenomorph/target_xenomorph = locate(params["target_ref"]) in GLOB.living_xeno_list + var/mob/living/carbon/xenomorph/xenomorph = ui.user + + if(QDELETED(target_xenomorph) || target_xenomorph.stat == DEAD || target_xenomorph.statistic_exempt) + return + + if(xenomorph.stat == DEAD) + if(isobserver(xenomorph)) + var/mob/dead/observer/O = xenomorph + O.ManualFollow(target_xenomorph) + return + + if(!xenomorph.check_state(TRUE)) + return + + xenomorph.overwatch(target_xenomorph) diff --git a/code/datums/factions/xeno/alpha.dm b/code/datums/factions/xeno/alpha.dm new file mode 100644 index 000000000000..e8355fd40b64 --- /dev/null +++ b/code/datums/factions/xeno/alpha.dm @@ -0,0 +1,8 @@ +/datum/faction/xenomorph/alpha + name = NAME_FACTION_XENOMORPH_ALPHA + faction_name = FACTION_XENOMORPH_ALPHA + organ_faction_iff_tag_type = /obj/item/faction_tag/organ/xenomorph/alpha + + prefix = "Alpha " + color = "#ff4040" + ui_color = "#992626" diff --git a/code/datums/factions/xeno/bravo.dm b/code/datums/factions/xeno/bravo.dm new file mode 100644 index 000000000000..82378a8d044d --- /dev/null +++ b/code/datums/factions/xeno/bravo.dm @@ -0,0 +1,8 @@ +/datum/faction/xenomorph/bravo + name = NAME_FACTION_XENOMORPH_BRAVO + faction_name = FACTION_XENOMORPH_BRAVO + organ_faction_iff_tag_type = /obj/item/faction_tag/organ/xenomorph/bravo + + prefix = "Bravo " + color = "#ffff80" + ui_color = "#99994d" diff --git a/code/datums/factions/xeno/charlie.dm b/code/datums/factions/xeno/charlie.dm new file mode 100644 index 000000000000..e86bc824b01d --- /dev/null +++ b/code/datums/factions/xeno/charlie.dm @@ -0,0 +1,8 @@ +/datum/faction/xenomorph/charlie + name = NAME_FACTION_XENOMORPH_CHARLIE + faction_name = FACTION_XENOMORPH_CHARLIE + organ_faction_iff_tag_type = /obj/item/faction_tag/organ/xenomorph/charlie + + prefix = "Charlie " + color = "#bb40ff" + ui_color = "#702699" diff --git a/code/datums/factions/xeno/corrupted.dm b/code/datums/factions/xeno/corrupted.dm new file mode 100644 index 000000000000..8ee780924efb --- /dev/null +++ b/code/datums/factions/xeno/corrupted.dm @@ -0,0 +1,40 @@ +/datum/faction/xenomorph/corrupted + name = NAME_FACTION_XENOMORPH_CORRUPTED + faction_name = FACTION_XENOMORPH_CORRUPTED + organ_faction_iff_tag_type = /obj/item/faction_tag/organ/xenomorph/corrupted + + prefix = "Corrupted " + color = "#80ff80" + ui_color ="#4d994d" + +/datum/faction/xenomorph/corrupted/add_mob(mob/living/carbon/xenomorph/X) + . = ..() + X.add_language(LANGUAGE_ENGLISH) + +/datum/faction/xenomorph/corrupted/remove_mob(mob/living/carbon/xenomorph/X, hard) + . = ..() + X.remove_language(LANGUAGE_ENGLISH) + + +/datum/faction/xenomorph/corrupted/tamed + name = NAME_FACTION_XENOMORPH_TAMED + faction_name = FACTION_XENOMORPH_TAMED + organ_faction_iff_tag_type = /obj/item/faction_tag/organ/xenomorph/tamed + + prefix = "Tamed " + color = "#80ff80" + + dynamic_evolution = FALSE + allow_no_queen_actions = TRUE + allow_queen_evolve = FALSE + ignore_slots = TRUE + +/datum/faction/xenomorph/corrupted/tamed/New() + . = ..() + faction_structures_limit[XENO_STRUCTURE_EGGMORPH] = 0 + faction_structures_limit[XENO_STRUCTURE_EVOPOD] = 0 + +/datum/faction/xenomorph/corrupted/tamed/add_mob(mob/living/carbon/xenomorph/xenomorph) + . = ..() + if(faction_leader) + xenomorph.faction = faction_leader.faction diff --git a/code/datums/factions/xeno/delta.dm b/code/datums/factions/xeno/delta.dm new file mode 100644 index 000000000000..10719842168d --- /dev/null +++ b/code/datums/factions/xeno/delta.dm @@ -0,0 +1,8 @@ +/datum/faction/xenomorph/delta + name = NAME_FACTION_XENOMORPH_DELTA + faction_name = FACTION_XENOMORPH_DELTA + organ_faction_iff_tag_type = /obj/item/faction_tag/organ/xenomorph/delta + + prefix = "Delta " + color = "#8080ff" + ui_color = "#4d4d99" diff --git a/code/datums/factions/xeno/feral.dm b/code/datums/factions/xeno/feral.dm new file mode 100644 index 000000000000..77ef854e7423 --- /dev/null +++ b/code/datums/factions/xeno/feral.dm @@ -0,0 +1,14 @@ +/datum/faction/xenomorph/feral + name = NAME_FACTION_XENOMORPH_FERAL + faction_name = FACTION_XENOMORPH_FERAL + organ_faction_iff_tag_type = /obj/item/faction_tag/organ/xenomorph/feral + + prefix = "Feral " + color = "#828296" + ui_color = "#828296" + + construction_allowed = XENO_QUEEN + dynamic_evolution = FALSE + allow_no_queen_actions = TRUE + allow_queen_evolve = FALSE + ignore_slots = TRUE diff --git a/code/datums/factions/xeno/forsaken.dm b/code/datums/factions/xeno/forsaken.dm new file mode 100644 index 000000000000..1a97ab18eac4 --- /dev/null +++ b/code/datums/factions/xeno/forsaken.dm @@ -0,0 +1,14 @@ +/datum/faction/xenomorph/forsaken + name = NAME_FACTION_XENOMORPH_FORSAKEN + faction_name = FACTION_XENOMORPH_FORSAKEN + organ_faction_iff_tag_type = /obj/item/faction_tag/organ/xenomorph/forsaken + + prefix = "Feral " + color = "#828296" + ui_color = "#828296" + + construction_allowed = XENO_QUEEN + dynamic_evolution = FALSE + allow_no_queen_actions = TRUE + allow_queen_evolve = FALSE + ignore_slots = TRUE diff --git a/code/datums/factions/xeno/mutated.dm b/code/datums/factions/xeno/mutated.dm new file mode 100644 index 000000000000..3e6eb87c6c54 --- /dev/null +++ b/code/datums/factions/xeno/mutated.dm @@ -0,0 +1,10 @@ +/datum/faction/xenomorph/mutated + name = NAME_FACTION_XENOMORPH_MUTATED + faction_name = FACTION_XENOMORPH_MUTATED + organ_faction_iff_tag_type = /obj/item/faction_tag/organ/xenomorph/mutated + + prefix = "Mutated " + color = "#6abd99" + ui_color = "#6abd99" + + hive_inherant_traits = list(TRAIT_XENONID) diff --git a/code/datums/factions/xeno/normal.dm b/code/datums/factions/xeno/normal.dm new file mode 100644 index 000000000000..4bc7d1b0c91b --- /dev/null +++ b/code/datums/factions/xeno/normal.dm @@ -0,0 +1,51 @@ +/datum/faction/xenomorph/normal + name = NAME_FACTION_XENOMORPH_NORMAL + faction_name = FACTION_XENOMORPH_NORMAL + organ_faction_iff_tag_type = /obj/item/faction_tag/organ/xenomorph/normal + + evolution_without_ovipositor = FALSE + color = null + ui_color = null + + role_mappings = list( + MODE_NAME_EXTENDED = list(), + MODE_NAME_DISTRESS_SIGNAL = list(), + MODE_NAME_FACTION_CLASH = list(), + MODE_NAME_CRASH = list(), + MODE_NAME_WISKEY_OUTPOST = list(), + MODE_NAME_HUNTER_GAMES = list(), + MODE_NAME_HIVE_WARS = list(), + MODE_NAME_INFECTION = list() + ) + roles_list = list( + MODE_NAME_EXTENDED = ROLES_REGULAR_XENO, + MODE_NAME_DISTRESS_SIGNAL = ROLES_REGULAR_XENO, + MODE_NAME_FACTION_CLASH = list(), + MODE_NAME_CRASH = list( + JOB_XENOMORPH + ), + MODE_NAME_WISKEY_OUTPOST = list( + JOB_XENOMORPH + ), + MODE_NAME_HUNTER_GAMES = list(), + MODE_NAME_HIVE_WARS = list( + JOB_XENOMORPH + ), + MODE_NAME_INFECTION = list( + JOB_XENOMORPH + ) + ) + coefficient_per_role = list( + JOB_XENOMORPH = 8, + JOB_XENOMORPH_QUEEN = 16 + ) + weight_act = list( + MODE_NAME_EXTENDED = FALSE, + MODE_NAME_DISTRESS_SIGNAL = TRUE, + MODE_NAME_FACTION_CLASH = FALSE, + MODE_NAME_CRASH = TRUE, + MODE_NAME_WISKEY_OUTPOST = FALSE, + MODE_NAME_HUNTER_GAMES = FALSE, + MODE_NAME_HIVE_WARS = TRUE, + MODE_NAME_INFECTION = FALSE + ) diff --git a/code/datums/factions/xeno/renegade.dm b/code/datums/factions/xeno/renegade.dm new file mode 100644 index 000000000000..636791820b3a --- /dev/null +++ b/code/datums/factions/xeno/renegade.dm @@ -0,0 +1,14 @@ +/datum/faction/xenomorph/renegade + name = NAME_FACTION_XENOMORPH_RENEGADE + faction_name = FACTION_XENOMORPH_RENEGADE + organ_faction_iff_tag_type = /obj/item/faction_tag/organ/xenomorph/renegade + + dynamic_evolution = FALSE + allow_no_queen_actions = TRUE + allow_queen_evolve = FALSE + ignore_slots = TRUE + + need_round_end_check = TRUE + +/datum/faction/xenomorph/renegade/can_delay_round_end(mob/living/carbon/C) + return FALSE diff --git a/code/datums/factions/xeno/yautja.dm b/code/datums/factions/xeno/yautja.dm new file mode 100644 index 000000000000..df506448dc77 --- /dev/null +++ b/code/datums/factions/xeno/yautja.dm @@ -0,0 +1,14 @@ +/datum/faction/xenomorph/yautja + name = NAME_FACTION_XENOMORPH_YAUTJA + faction_name = FACTION_XENOMORPH_YAUTJA + organ_faction_iff_tag_type = /obj/item/faction_tag/organ/xenomorph/yautja + + dynamic_evolution = FALSE + allow_no_queen_actions = TRUE + allow_queen_evolve = FALSE + ignore_slots = TRUE + + need_round_end_check = TRUE + +/datum/faction/xenomorph/yautja/can_delay_round_end(mob/living/carbon/C) + return FALSE diff --git a/code/datums/factions/yautja.dm b/code/datums/factions/yautja.dm new file mode 100644 index 000000000000..ee314fc8ad8d --- /dev/null +++ b/code/datums/factions/yautja.dm @@ -0,0 +1,47 @@ +/datum/faction/yautja + name = NAME_FACTION_YAUTJA + desc = "Unable to extract addition information." + + faction_name = FACTION_YAUTJA + faction_tag = SIDE_FACTION_YAUTJA + relations_pregen = RELATIONS_HOSTILE + + role_mappings = list( + MODE_NAME_EXTENDED = list(), + MODE_NAME_DISTRESS_SIGNAL = list(), + MODE_NAME_FACTION_CLASH = list(), + MODE_NAME_CRASH = list(), + MODE_NAME_WISKEY_OUTPOST = list(), + MODE_NAME_HUNTER_GAMES = list(), + MODE_NAME_HIVE_WARS = list(), + MODE_NAME_INFECTION = list() + ) + roles_list = list( + MODE_NAME_EXTENDED = ROLES_REGULAR_YAUT, + MODE_NAME_DISTRESS_SIGNAL = ROLES_REGULAR_YAUT, + MODE_NAME_FACTION_CLASH = ROLES_REGULAR_YAUT, + MODE_NAME_CRASH = list(), + MODE_NAME_WISKEY_OUTPOST = list(), + MODE_NAME_HUNTER_GAMES = ROLES_REGULAR_YAUT, + MODE_NAME_HIVE_WARS = list(), + MODE_NAME_INFECTION = ROLES_REGULAR_YAUT + ) + weight_act = list( + MODE_NAME_EXTENDED = FALSE, + MODE_NAME_DISTRESS_SIGNAL = FALSE, + MODE_NAME_FACTION_CLASH = FALSE, + MODE_NAME_CRASH = FALSE, + MODE_NAME_WISKEY_OUTPOST = FALSE, + MODE_NAME_HUNTER_GAMES = FALSE, + MODE_NAME_HIVE_WARS = FALSE, + MODE_NAME_INFECTION = FALSE + ) + +/datum/faction/yautja/get_join_status(mob/new_player/user, dat) + if(alert(user, user.client.auto_lang(LANGUAGE_LOBBY_JOIN_HUNT), user.client.auto_lang(LANGUAGE_CONFIRM), user.client.auto_lang(LANGUAGE_YES), user.client.auto_lang(LANGUAGE_NO)) == user.client.auto_lang(LANGUAGE_YES)) + if(SSticker.mode.check_predator_late_join(user, 0)) + user.close_spawn_windows() + SSticker.mode.attempt_to_join_as_predator(user) + else + to_chat(user, SPAN_WARNING(user.client.auto_lang(LANGUAGE_LOBBY_NO_JOIN_HUNT))) + user.new_player_panel() diff --git a/code/datums/factions/zombie.dm b/code/datums/factions/zombie.dm new file mode 100644 index 000000000000..a0aeae97019d --- /dev/null +++ b/code/datums/factions/zombie.dm @@ -0,0 +1,60 @@ +/datum/faction/zombie + name = NAME_FACTION_ZOMBIE + desc = "Look up in WY database... Unknow virus that makes all dead rise back and fight, for additional information required access 6X-X / XC-X or higher..." + + faction_name = FACTION_ZOMBIE + faction_tag = SIDE_FACTION_ZOMBIE + relations_pregen = RELATIONS_MAP_HOSTILE + organ_faction_iff_tag_type = /obj/item/faction_tag/organ/zombie + +/datum/faction/zombie/get_join_status(mob/user, dat) + if(!user.client) + return + + if(SSticker.current_state < GAME_STATE_PLAYING || !SSticker.mode) + to_chat(src, SPAN_WARNING("The game hasn't started yet!")) + return + + var/list/zombie_list = list() + if(length(GLOB.zombie_landmarks)) + zombie_list += list("Underground Zombie" = "Underground Zombie") + + for(var/mob/living/carbon/human/A in GLOB.zombie_list) + if(!A.client && A.stat != DEAD) // Only living zombies + zombie_list += list(A.real_name = A) + + if(!length(zombie_list)) + to_chat(src, SPAN_DANGER("There are no available zombies.")) + return + + var/choice = tgui_input_list(usr, "Pick a Zombie:", "Join as Zombie", zombie_list) + if(!choice) + return + + if(!user.client || !user.mind) + return + + if(choice == "Underground Zombie") + if(!length(GLOB.zombie_landmarks)) + to_chat(src, SPAN_WARNING("Sorry, the last underground zombie just got taken.")) + return + + var/obj/effect/landmark/zombie/spawn_point = pick(GLOB.zombie_landmarks) + spawn_point.spawn_zombie(src) + return + + var/mob/living/carbon/human/Z = zombie_list[choice] + + if(!Z || QDELETED(Z)) + return + + if(Z.stat == DEAD) + to_chat(src, SPAN_WARNING("This zombie is dead!")) + return + + if(Z.client) + to_chat(src, SPAN_WARNING("That player is still connected.")) + return + + user.mind.transfer_to(Z, TRUE) + msg_admin_niche("[key_name(usr)] has joined as a [Z].") diff --git a/code/defines/procs/announcement.dm b/code/defines/procs/announcement.dm index 250c81dfc3dc..72480abfee5c 100644 --- a/code/defines/procs/announcement.dm +++ b/code/defines/procs/announcement.dm @@ -1,118 +1,75 @@ -#define COMMAND_ANNOUNCE "Command Announcement" -#define UPP_COMMAND_ANNOUNCE "UPP Command Announcement" -#define CLF_COMMAND_ANNOUNCE "CLF Command Announcement" -#define PMC_COMMAND_ANNOUNCE "PMC Command Announcement" -#define QUEEN_ANNOUNCE "The words of the Queen reverberate in your head..." -#define QUEEN_MOTHER_ANNOUNCE "Queen Mother Psychic Directive" -#define XENO_GENERAL_ANNOUNCE "You sense something unusual..." //general xeno announcement that don't involve Queen, for nuke for example -#define YAUTJA_ANNOUNCE "You receive a message from your ship AI..." //preds announcement -#define HIGHER_FORCE_ANNOUNCE SPAN_ANNOUNCEMENT_HEADER_BLUE("Unknown Higher Force") - //xenomorph hive announcement -/proc/xeno_announcement(message, hivenumber, title = QUEEN_ANNOUNCE) - var/list/targets = GLOB.living_xeno_list + GLOB.dead_mob_list - if(hivenumber == "everything") - for(var/mob/M in targets) - var/mob/living/carbon/xenomorph/X = M - if(!isobserver(X) && !istype(X)) //filter out any potential non-xenomorphs/observers mobs - targets.Remove(X) - - announcement_helper(message, title, targets, sound(get_sfx("queen"),wait = 0,volume = 50)) +/proc/xeno_announcement(message, datum/faction/faction_to_display = GLOB.faction_datum[FACTION_XENOMORPH_NORMAL], title = QUEEN_ANNOUNCE) + var/list/targets = GLOB.dead_mob_list.Copy() + if(faction_to_display == "Everyone") + for(var/faction_to_get in FACTION_LIST_XENOMORPH) + var/datum/faction/faction = GLOB.faction_datum[faction_to_get] + for(var/mob/mob as anything in faction.totalMobs) + if(mob.stat != CONSCIOUS) + continue + targets.Add(mob) else - for(var/mob/M in targets) - if(isobserver(M)) - continue - var/mob/living/carbon/X = M - if(!istype(X) || !X.ally_of_hivenumber(hivenumber)) //additionally filter out those of wrong hive - targets.Remove(X) - - announcement_helper(message, title, targets, sound(get_sfx("queen"),wait = 0,volume = 50)) - - -//general marine announcement -/proc/marine_announcement(message, title = COMMAND_ANNOUNCE, sound_to_play = sound('sound/misc/notice2.ogg'), faction_to_display = FACTION_MARINE, add_PMCs = TRUE, signature, logging = ARES_LOG_MAIN) - var/list/targets = GLOB.human_mob_list + GLOB.dead_mob_list - if(faction_to_display == FACTION_MARINE) - for(var/mob/M in targets) - if(isobserver(M)) //observers see everything - continue - var/mob/living/carbon/human/H = M - if(!istype(H) || H.stat != CONSCIOUS || isyautja(H)) //base human checks - targets.Remove(H) + for(var/mob/mob as anything in faction_to_display.totalMobs) + if(mob.stat != CONSCIOUS) continue - if(is_mainship_level(H.z)) // People on ship see everything - continue - - // If they have iff AND a marine headset they will recieve announcements - var/obj/item/card/id/card = H.get_idcard() - if ((FACTION_MARINE in card?.faction_group) && (istype(H.wear_l_ear, /obj/item/device/radio/headset/almayer) || istype(H.wear_r_ear, /obj/item/device/radio/headset/almayer))) - continue - - if((H.faction != faction_to_display && !add_PMCs) || (H.faction != faction_to_display && add_PMCs && !(H.faction in FACTION_LIST_WY)) && !(faction_to_display in H.faction_group)) //faction checks - targets.Remove(H) - - switch(logging) - if(ARES_LOG_MAIN) - log_ares_announcement(title, message, signature) - if(ARES_LOG_SECURITY) - log_ares_security(title, message, signature) - - else if(faction_to_display == "Everyone (-Yautja)") - for(var/mob/M in targets) - if(isobserver(M)) //observers see everything - continue - var/mob/living/carbon/human/H = M - if(!istype(H) || H.stat != CONSCIOUS || isyautja(H)) - targets.Remove(H) + targets.Add(mob) + + announcement_helper(message, title, targets, sound(get_sfx("queen"), wait = 0, volume = 50)) + + +//general faction announcement +/proc/faction_announcement(message, title = COMMAND_ANNOUNCE, sound_to_play = sound('sound/misc/notice2.ogg'), datum/faction/faction_to_display = GLOB.faction_datum[FACTION_MARINE], signature, logging = ARES_LOG_MAIN) + var/list/targets = GLOB.dead_mob_list.Copy() + if(faction_to_display == GLOB.faction_datum[FACTION_MARINE]) + var/datum/ares_link/link = GLOB.ares_link + if(ares_can_log()) + switch(logging) + if(ARES_LOG_MAIN) + link.log_ares_announcement(title, message) + if(ARES_LOG_SECURITY) + link.log_ares_security(title, message) + + if(faction_to_display == "Everyone (-Yautja)") + for(var/faction_to_get in FACTION_LIST_HUMANOID - FACTION_YAUTJA) + var/datum/faction/faction = GLOB.faction_datum[faction_to_get] + for(var/mob/mob as anything in faction.totalMobs) + if(mob.stat != CONSCIOUS) + continue + targets.Add(mob) else - for(var/mob/M in targets) - if(isobserver(M)) //observers see everything - continue - var/mob/living/carbon/human/H = M - if(!istype(H) || H.stat != CONSCIOUS || isyautja(H)) - targets.Remove(H) + for(var/mob/mob as anything in faction_to_display.totalMobs) + if(mob.stat != CONSCIOUS) continue - if(H.faction != faction_to_display) - targets.Remove(H) + targets.Add(mob) if(!isnull(signature)) message += "

Signed by,
[signature]
" announcement_helper(message, title, targets, sound_to_play) -//yautja ship AI announcement -/proc/yautja_announcement(message, title = YAUTJA_ANNOUNCE, sound_to_play = sound('sound/misc/notice1.ogg')) - var/list/targets = GLOB.human_mob_list + GLOB.dead_mob_list - for(var/mob/M in targets) - if(isobserver(M)) //observers see everything - continue - var/mob/living/carbon/human/H = M - if(!isyautja(H) || H.stat != CONSCIOUS) - targets.Remove(H) - - announcement_helper(message, title, targets, sound_to_play) - //AI announcement that uses talking into comms /proc/ai_announcement(message, sound_to_play = sound('sound/misc/interference.ogg'), logging = ARES_LOG_MAIN) - for(var/mob/M in (GLOB.human_mob_list + GLOB.dead_mob_list)) - if((isobserver(M) && M.client?.prefs?.toggles_sound & SOUND_OBSERVER_ANNOUNCEMENTS) || ishuman(M) && is_mainship_level(M.z)) + for(var/mob/M as anything in (GLOB.human_mob_list + GLOB.dead_mob_list)) + if(isobserver(M) || ishuman(M) && is_mainship_level(M.z)) playsound_client(M.client, sound_to_play, M, vol = 45) - for(var/mob/living/silicon/decoy/ship_ai/AI in GLOB.ai_mob_list) + for(var/mob/living/silicon/decoy/ship_ai/AI in ai_mob_list) INVOKE_ASYNC(AI, TYPE_PROC_REF(/mob/living/silicon/decoy/ship_ai, say), message) - switch(logging) - if(ARES_LOG_MAIN) - log_ares_announcement("Comms Update", message, MAIN_AI_SYSTEM) - if(ARES_LOG_SECURITY) - log_ares_security("Security Update", message, MAIN_AI_SYSTEM) + var/datum/ares_link/link = GLOB.ares_link + if(ares_can_log()) + switch(logging) + if(ARES_LOG_MAIN) + link.log_ares_announcement("[MAIN_AI_SYSTEM] Comms Update", message) + if(ARES_LOG_SECURITY) + link.log_ares_security("[MAIN_AI_SYSTEM] Security Update", message) /proc/ai_silent_announcement(message, channel_prefix, bypass_cooldown = FALSE) if(!message) return - for(var/mob/living/silicon/decoy/ship_ai/AI in GLOB.ai_mob_list) + for(var/mob/living/silicon/decoy/ship_ai/AI in ai_mob_list) if(channel_prefix) message = "[channel_prefix][message]" INVOKE_ASYNC(AI, TYPE_PROC_REF(/mob/living/silicon/decoy/ship_ai, say), message) @@ -127,34 +84,34 @@ //AI shipside announcement, that uses announcement mechanic instead of talking into comms //to ensure that all humans on ship hear it regardless of comms and power -/proc/shipwide_ai_announcement(message, title = MAIN_AI_SYSTEM, sound_to_play = sound('sound/misc/interference.ogg'), signature, ares_logging = ARES_LOG_MAIN) +/proc/shipwide_ai_announcement(message, title = MAIN_AI_SYSTEM, sound_to_play = sound('sound/misc/interference.ogg'), signature) var/list/targets = GLOB.human_mob_list + GLOB.dead_mob_list - for(var/mob/T in targets) - if(isobserver(T)) + for(var/mob/mob in targets) + if(isobserver(mob)) continue - if(!ishuman(T) || isyautja(T) || !is_mainship_level(T.z)) - targets.Remove(T) + if(!ishuman(mob) || isyautja(mob) || !is_mainship_level(mob.z)) + targets.Remove(mob) if(!isnull(signature)) message += "

Signed by,
[signature]
" - switch(ares_logging) - if(ARES_LOG_MAIN) - log_ares_announcement(title, message, signature) - if(ARES_LOG_SECURITY) - log_ares_security(title, message, signature) + var/datum/ares_link/link = GLOB.ares_link + if(link.interface && !(link.interface.inoperable())) + link.log_ares_announcement(title, message) announcement_helper(message, title, targets, sound_to_play) //Subtype of AI shipside announcement for "All Hands On Deck" alerts (COs and SEAs joining the game) /proc/all_hands_on_deck(message, title = MAIN_AI_SYSTEM, sound_to_play = sound('sound/misc/sound_misc_boatswain.ogg')) var/list/targets = GLOB.human_mob_list + GLOB.dead_mob_list - for(var/mob/T in targets) - if(isobserver(T)) + for(var/mob/mob in targets) + if(isobserver(mob)) continue - if(!ishuman(T) || isyautja(T) || !is_mainship_level((get_turf(T))?.z)) - targets.Remove(T) + if(!ishuman(mob) || isyautja(mob) || !is_mainship_level(mob.z)) + targets.Remove(mob) - log_ares_announcement("Shipwide Update", message, title) + var/datum/ares_link/link = GLOB.ares_link + if(ares_can_log()) + link.log_ares_announcement("[title] Shipwide Update", message) announcement_helper(message, title, targets, sound_to_play) @@ -162,11 +119,9 @@ /proc/announcement_helper(message, title, list/targets, sound_to_play) if(!message || !title || !sound_to_play || !targets) //Shouldn't happen return - for(var/mob/T in targets) - if(istype(T, /mob/new_player)) + for(var/mob/mob in targets) + if(istype(mob, /mob/new_player)) continue - to_chat_spaced(T, html = "[SPAN_ANNOUNCEMENT_HEADER(title)]

[SPAN_ANNOUNCEMENT_BODY(message)]", type = MESSAGE_TYPE_RADIO) - if(isobserver(T) && !(T.client?.prefs?.toggles_sound & SOUND_OBSERVER_ANNOUNCEMENTS)) - continue - playsound_client(T.client, sound_to_play, T, vol = 45) + to_chat_spaced(mob, html = "[SPAN_ANNOUNCEMENT_HEADER(title)]

[SPAN_ANNOUNCEMENT_BODY(message)]", type = MESSAGE_TYPE_RADIO) + playsound_client(mob.client, sound_to_play, mob, vol = 45) diff --git a/code/modules/admin/tabs/event_tab.dm b/code/modules/admin/tabs/event_tab.dm index a155f2a37614..a73617c75449 100644 --- a/code/modules/admin/tabs/event_tab.dm +++ b/code/modules/admin/tabs/event_tab.dm @@ -6,41 +6,38 @@ to_chat(usr, "Only administrators may use this command.") return - if(!LAZYLEN(GLOB.custom_event_info_list)) + if(!length(GLOB.custom_event_info_list)) to_chat(usr, "custom_event_info_list is not initialized, tell a dev.") return var/list/temp_list = list() for(var/T in GLOB.custom_event_info_list) - var/datum/custom_event_info/CEI = GLOB.custom_event_info_list[T] - temp_list["[CEI.msg ? "(x) [CEI.faction]" : CEI.faction]"] = CEI.faction + var/datum/custom_event_info/custom_event = GLOB.custom_event_info_list[T] + temp_list["[custom_event.msg ? "(x) [custom_event.faction_name]" : custom_event.faction_name]"] = custom_event.faction_name - var/faction = tgui_input_list(usr, "Select faction. Ghosts will see only \"Global\" category message. Factions with event message set are marked with (x).", "Faction Choice", temp_list) - if(!faction) + var/event_info_get = temp_list[tgui_input_list(usr, "Select faction. Ghosts will see only \"Global\" category message. Factions with event message set are marked with (x).", "Faction Choice", temp_list)] + if(!event_info_get) return - faction = temp_list[faction] - - if(!GLOB.custom_event_info_list[faction]) - to_chat(usr, "Error has occurred, [faction] category is not found.") + var/datum/custom_event_info/custom_info = GLOB.custom_event_info_list[event_info_get] + if(!custom_info) + to_chat(usr, "custom_event_info_list don't have [event_info_get] in list, tell a dev.") return - var/datum/custom_event_info/CEI = GLOB.custom_event_info_list[faction] - - var/input = input(usr, "Enter the custom event message for \"[faction]\" category. Be descriptive. \nTo remove the event message, remove text and confirm.", "[faction] Event Message", CEI.msg) as message|null + var/input = tgui_input_text(usr, "Enter the custom event message for \"[event_info_get]\" category. Be descriptive. \nTo remove the event message, remove text and confirm.", "[event_info_get] Event Message", custom_info.msg, 4096, TRUE) if(isnull(input)) return if(input == "" || !input) - CEI.msg = "" - message_admins("[key_name_admin(usr)] has removed the event message for \"[faction]\" category.") + custom_info.msg = "" + message_admins("[key_name_admin(usr)] has removed the event message for \"[event_info_get]\" category.") return - CEI.msg = html_encode(input) - message_admins("[key_name_admin(usr)] has changed the event message for \"[faction]\" category.") + custom_info.msg = html_encode(input) + message_admins("[key_name_admin(usr)] has changed the event message for \"[event_info_get]\" category.") - CEI.handle_event_info_update(faction) + custom_info.handle_event_info_update() /client/proc/change_security_level() if(!check_rights(R_ADMIN)) diff --git a/code/modules/admin/verbs/custom_event.dm b/code/modules/admin/verbs/custom_event.dm index eaba95481d16..42b8085f3bf2 100644 --- a/code/modules/admin/verbs/custom_event.dm +++ b/code/modules/admin/verbs/custom_event.dm @@ -6,33 +6,20 @@ var/category = "Global" - var/datum/custom_event_info/CEI + var/datum/custom_event_info/custom_event if(mob && !isobserver(mob) && !isnewplayer(mob)) - - if(isxeno(mob)) - var/mob/living/carbon/xenomorph/X = mob - if(!X.hive || !GLOB.custom_event_info_list[X.hive.name]) - to_chat(src, SPAN_WARNING("\n\n[X] has none or incorrect hive set or hive message datum was not found, tell a dev!\n\n")) - CEI = GLOB.custom_event_info_list["Global"] - else - category = tgui_input_list(src, "Select category.", "Custom Event Info", list("Global", X.hive.name)) - CEI = GLOB.custom_event_info_list[category] - - CEI.show_player_event_info(src) - return - - else if(!mob.faction || !GLOB.custom_event_info_list[mob.faction]) + if(!mob.faction || !GLOB.custom_event_info_list[mob.faction.faction_name]) to_chat(src, SPAN_WARNING("\n\n[mob] has none or incorrect faction set or faction message datum was not found, tell a dev!\n\n")) - CEI = GLOB.custom_event_info_list["Global"] + custom_event = GLOB.custom_event_info_list["Global"] else - category = tgui_input_list(src, "Select category.", "Custom Event Info", list("Global", mob.faction)) - CEI = GLOB.custom_event_info_list[category] + category = tgui_input_list(src, "Select category.", "Custom Event Info", list("Global", mob.faction.faction_name)) + custom_event = GLOB.custom_event_info_list[category] - CEI.show_player_event_info(src) + custom_event.show_player_event_info(src) return else - CEI = GLOB.custom_event_info_list["Global"] - CEI.show_player_event_info(src) + custom_event = GLOB.custom_event_info_list["Global"] + custom_event.show_player_event_info(src) return diff --git a/colonialmarines.dme b/colonialmarines.dme index 9bfccac6a56e..5aab5ce932f3 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -57,6 +57,7 @@ #include "code\__DEFINES\emote_panels.dm" #include "code\__DEFINES\equipment.dm" #include "code\__DEFINES\events.dm" +#include "code\__DEFINES\factions.dm" #include "code\__DEFINES\fire.dm" #include "code\__DEFINES\flags.dm" #include "code\__DEFINES\fonts.dm" @@ -563,13 +564,46 @@ #include "code\datums\entities\logs\player_times_log.dm" #include "code\datums\factions\clf.dm" #include "code\datums\factions\cmb.dm" +#include "code\datums\factions\colonists.dm" #include "code\datums\factions\contractor.dm" +#include "code\datums\factions\dutch's_dozen.dm" #include "code\datums\factions\faction.dm" -#include "code\datums\factions\helpers.dm" -#include "code\datums\factions\pmc.dm" -#include "code\datums\factions\royalmarinescommando.dm" +#include "code\datums\factions\freelancer.dm" +#include "code\datums\factions\gladiator.dm" +#include "code\datums\factions\hefa_order.dm" +#include "code\datums\factions\mercenary.dm" +#include "code\datums\factions\pirate.dm" +#include "code\datums\factions\pizza_delivery.dm" +#include "code\datums\factions\ress.dm" +#include "code\datums\factions\souto.dm" +#include "code\datums\factions\twe.dm" #include "code\datums\factions\upp.dm" #include "code\datums\factions\uscm.dm" +#include "code\datums\factions\wy.dm" +#include "code\datums\factions\xeno.dm" +#include "code\datums\factions\yautja.dm" +#include "code\datums\factions\zombie.dm" +#include "code\datums\factions\__FACTION_MODULES\hive.dm" +#include "code\datums\factions\__misc\faction_tasks.dm" +#include "code\datums\factions\__misc\helpers.dm" +#include "code\datums\factions\__misc\relations_actions.dm" +#include "code\datums\factions\__misc\relations_datum.dm" +#include "code\datums\factions\uscm\cmb.dm" +#include "code\datums\factions\uscm\marine.dm" +#include "code\datums\factions\uscm\marsoc.dm" +#include "code\datums\factions\wy\deathsquad.dm" +#include "code\datums\factions\wy\pmc.dm" +#include "code\datums\factions\xeno\alpha.dm" +#include "code\datums\factions\xeno\bravo.dm" +#include "code\datums\factions\xeno\charlie.dm" +#include "code\datums\factions\xeno\corrupted.dm" +#include "code\datums\factions\xeno\delta.dm" +#include "code\datums\factions\xeno\feral.dm" +#include "code\datums\factions\xeno\forsaken.dm" +#include "code\datums\factions\xeno\mutated.dm" +#include "code\datums\factions\xeno\normal.dm" +#include "code\datums\factions\xeno\renegade.dm" +#include "code\datums\factions\xeno\yautja.dm" #include "code\datums\helper_datums\getrev.dm" #include "code\datums\helper_datums\stack_end_detector.dm" #include "code\datums\helper_datums\teleport.dm"