Files
2024-01-05 00:28:56 -03:00

300 lines
15 KiB
Plaintext

GLOBAL_VAR_INIT(servants_active, FALSE) //This var controls whether or not a lot of the cult's structures work or not
/*
CLOCKWORK CULT: Based off of the failed pull requests from /vg/
While Nar'Sie is the oldest and most prominent of the elder gods, there are other forces at work in the universe.
Ratvar, the Clockwork Justiciar, a homage to Nar'Sie granted sentience by its own power, is one such other force.
Imprisoned within a massive construct known as the Celestial Derelict - or Reebe - an intense hatred of the Blood God festers.
Ratvar, unable to act in the mortal plane, seeks to return and forms covenants with mortals in order to bolster his influence.
Due to his mechanical nature, Ratvar is also capable of influencing silicon-based lifeforms, unlike Nar'Sie, who can only influence natural life.
This is a team-based gamemode, and the team's objective is shared by all cultists. Their goal is to defend an object called the Ark on a separate z-level.
The clockwork version of an arcane tome is the clockwork slab.
This file's folder contains:
clock_cult.dm: Core gamemode files.
clock_effect.dm: The base clockwork effect code.
- Effect files are in game/gamemodes/clock_cult/clock_effects/
clock_item.dm: The base clockwork item code.
- Item files are in game/gamemodes/clock_cult/clock_items/
clock_mobs.dm: Hostile clockwork creatures.
clock_scripture.dm: The base Scripture code.
- Scripture files are in game/gamemodes/clock_cult/clock_scripture/
clock_structure.dm: The base clockwork structure code, including clockwork machines.
- Structure files, and Ratvar, are in game/gamemodes/clock_cult/clock_structures/
game/gamemodes/clock_cult/clock_helpers/ contains several helper procs, including the Ratvarian language.
clockcult defines are in __DEFINES/clockcult.dm
Credit where due:
1. VelardAmakar from /vg/ for the entire design document, idea, and plan. Thank you very much.
2. SkowronX from /vg/ for MANY of the assets
3. FuryMcFlurry from /vg/ for many of the assets
4. PJB3005 from /vg/ for the failed continuation PR
5. Xhuis from /tg/ for coding the first iteration of the mode, and the new, reworked version
6. ChangelingRain from /tg/ for maintaining the gamemode for months after its release prior to its rework
7. Clockwork cult code as of now, at least the one being pulled from Citadel Station's master branch, is being, or already is, fixed by Coolgat3 and Avunia.
8. Modern clockwork cult code mixed with original clockwork code, with various changes to make it less of a fustercluck, done by KeRSe. \
Fixes and assistance done by TimothyTeakettle, Kevinz000, and Deltafire15. -Very glad for the help they gave.
*/
///////////
// PROCS //
///////////
/proc/is_servant_of_ratvar(mob/M, require_full_power = FALSE, holy_water_check = FALSE)
if(!istype(M) || isobserver(M))
return FALSE
var/datum/antagonist/clockcult/D = M?.mind?.has_antag_datum(/datum/antagonist/clockcult)
return D && (!require_full_power || !D.neutered) && (!holy_water_check || !D.ignore_holy_water)
/proc/is_eligible_servant(mob/M)
if(!istype(M))
return FALSE
if(M.mind)
if(M.mind.assigned_role in list("Captain", "Chaplain"))
return FALSE
if(M.mind.enslaved_to && !is_servant_of_ratvar(M.mind.enslaved_to))
return FALSE
if(M.mind.unconvertable)
return FALSE
else
return FALSE
if(iscultist(M) || isconstruct(M) || ispAI(M))
return FALSE
if(isliving(M))
var/mob/living/L = M
if(HAS_TRAIT(L, TRAIT_MINDSHIELD))
return FALSE
if(ishuman(M) || isbrain(M) || isguardian(M) || issilicon(M) || isclockmob(M) || istype(M, /mob/living/simple_animal/drone/cogscarab) || istype(M, /mob/camera/eminence))
return TRUE
return FALSE
/proc/add_servant_of_ratvar(mob/L, silent = FALSE, create_team = TRUE, override_type)
if(!L || !L.mind)
return
var/update_type = /datum/antagonist/clockcult
if(silent)
update_type = /datum/antagonist/clockcult/silent
if(override_type) //prioritizes
update_type = override_type
var/datum/antagonist/clockcult/C = new update_type(L.mind)
C.make_team = create_team
C.show_in_roundend = create_team //tutorial scarabs begone
if(iscyborg(L))
var/mob/living/silicon/robot/R = L
if(R.deployed)
var/mob/living/silicon/ai/AI = R.mainframe
R.undeploy()
to_chat(AI, "<span class='userdanger'>Anomaly Detected. Returned to core!</span>") //The AI needs to be in its core to properly be converted
. = L.mind.add_antag_datum(C)
if(!silent && L)
if(.)
to_chat(L, "<span class='heavy_brass'>The world before you suddenly glows a brilliant yellow. [issilicon(L) ? "You cannot compute this truth!" : \
"Your mind is racing!"] You hear the whooshing steam and cl[pick("ank", "ink", "unk", "ang")]ing cogs of a billion billion machines, and all at once it comes to you.<br>\
Ratvar, the Clockwork Justiciar, [GLOB.ratvar_awakens ? "has been freed from his eternal prison" : "lies in exile, derelict and forgotten in an unseen realm"].</span>")
flash_color(L, flash_color = list("#BE8700", "#BE8700", "#BE8700", rgb(0,0,0)), flash_time = 50)
else
L.visible_message("<span class='boldwarning'>[L] seems to resist an unseen force!</span>", null, null, 7, L)
to_chat(L, "<span class='heavy_brass'>The world before you suddenly glows a brilliant yellow. [issilicon(L) ? "You cannot compute this truth!" : \
"Your mind is racing!"] You hear the whooshing steam and cl[pick("ank", "ink", "unk", "ang")]ing cogs of a billion billion machines, and the sound</span> <span class='boldwarning'>\
is a meaningless cacophony.</span><br>\
<span class='userdanger'>You see an abomination of rusting parts[GLOB.ratvar_awakens ? ", and it is here.<br>It is too late" : \
" in an endless grey void.<br>It cannot be allowed to escape"].</span>")
L.playsound_local(get_turf(L), 'sound/ambience/antag/clockcultalr.ogg', 40, TRUE, frequency = 100000, pressure_affected = FALSE)
flash_color(L, flash_color = list("#BE8700", "#BE8700", "#BE8700", rgb(0,0,0)), flash_time = 5)
/proc/remove_servant_of_ratvar(mob/L, silent = FALSE)
if(!L || !L.mind)
return
var/datum/antagonist/clockcult/clock_datum = L.mind.has_antag_datum(/datum/antagonist/clockcult)
if(!clock_datum)
return FALSE
clock_datum.silent = silent
clock_datum.on_removal()
return TRUE
///////////////
// GAME MODE //
///////////////
/datum/game_mode
var/list/servants_of_ratvar = list() //The Enlightened servants of Ratvar
var/clockwork_explanation = "Defend the Ark of the Clockwork Justiciar and free Ratvar." //The description of the current objective
/datum/game_mode/clockwork_cult
name = "clockwork cult"
config_tag = "clockwork_cult"
antag_flag = ROLE_SERVANT_OF_RATVAR
false_report_weight = 10
chaos = 8
required_players = 24 //Fixing this directly for now since apparently config machine for forcing modes broke.
required_enemies = 3
recommended_enemies = 5
enemy_minimum_age = 7
protected_jobs = list("Prisoner", "AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") //Silicons can eventually be converted
restricted_jobs = list("Chaplain", "Captain")
announce_span = "brass"
announce_text = "Servants of Ratvar are trying to summon the Justiciar!\n\
<span class='brass'>Servants</span>: Construct defenses to protect the Ark. Sabotage the station!\n\
<span class='notice'>Crew</span>: Stop the servants before they can summon the Clockwork Justiciar."
var/list/servants_to_serve = list() //Yes this list is made out of list
var/roundstart_player_count
var/datum/team/clockcult/main_clockcult
/datum/game_mode/clockwork_cult/pre_setup() //Gamemode and job code is pain. Have fun codediving all of that stuff, whoever works on this next - Delta
if(!load_reebe())
return FALSE
if(CONFIG_GET(flag/protect_roles_from_antagonist))
restricted_jobs += protected_jobs
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
restricted_jobs += "Assistant"
var/starter_servants = 4 //Try to go for at least four
var/number_players = num_players()
roundstart_player_count = number_players
if(number_players > 30) //plus one servant for every additional 10 players above 30
number_players -= 30
starter_servants += round(number_players / 10)
starter_servants = min(starter_servants, 8) //max 8 servants (that sould only happen with a ton of players)
while(starter_servants)
if(!antag_candidates.len)
break //Skip setup, DO NOT RUNTIME
var/datum/mind/servant = antag_pick(antag_candidates)
servants_to_serve += servant
antag_candidates -= servant
servant.special_role = ROLE_SERVANT_OF_RATVAR
servant.restricted_roles = restricted_jobs
starter_servants--
if(!servants_to_serve.len) //Uh oh, something went wrong
setup_error = "There are no clockcult candidates! (Or something went very wrong)"
return FALSE
GLOB.clockwork_vitality += 50 * servants_to_serve.len //some starter Vitality to help recover from initial fuck ups
return TRUE //Haha yes it works time to not touch it any more than that.
/datum/game_mode/clockwork_cult/post_setup()
for(var/S in servants_to_serve)
var/datum/mind/servant = S
log_game("[key_name(servant)] was made an initial servant of Ratvar")
var/mob/living/L = servant.current
greet_servant(L)
equip_servant(L)
add_servant_of_ratvar(L, TRUE)
..()
return TRUE
/datum/game_mode/proc/greet_servant(mob/M) //Description of their role
if(!M)
return FALSE
to_chat(M, "<span class='bold large_brass'>You are a servant of Ratvar, the Clockwork Justiciar!</span>")
to_chat(M, "<span class='brass'>Unlock <b>Script</b> scripture by converting a new servant or when 35kw of power is reached.</span>")
to_chat(M, "<span class='brass'><b>Application</b> scripture will be unlocked when 50kw of power is reached.</span>")
M.playsound_local(get_turf(M), 'sound/ambience/antag/clockcultalr.ogg', 100, FALSE, pressure_affected = FALSE)
return TRUE
/datum/game_mode/proc/equip_servant(mob/living/M) //Grants a clockwork slab to the mob
if(!M || !ishuman(M))
return FALSE
var/mob/living/carbon/human/L = M
var/obj/item/clockwork/slab/S = new
var/slot = "At your feet"
var/list/slots = list("In your left pocket" = ITEM_SLOT_LPOCKET, "In your right pocket" = ITEM_SLOT_RPOCKET, "In your backpack" = ITEM_SLOT_BACKPACK)
if(ishuman(L))
var/mob/living/carbon/human/H = L
var/obj/item/clockwork/replica_fabricator/F = new
if(H.equip_to_slot_or_del(F, ITEM_SLOT_BACKPACK))
to_chat(H, "<span class='brass'>You have been equipped with a replica fabricator, an advanced tool that can convert objects like doors, tables or even coats into clockwork equivalents.</span>")
slot = H.equip_in_one_of_slots(S, slots)
if(slot == "In your backpack")
slot = "In your [H.back.name]"
if(slot == "At your feet")
if(!S.forceMove(get_turf(L)))
qdel(S)
if(S && !QDELETED(S))
to_chat(L, "<span class='alloy'>[slot] is a <b>clockwork slab</b>, a multipurpose tool used to construct machines and invoke ancient words of power. If this is your first time \
as a servant, you can read <a href=\"https://citadel-station.net/wikimain/index.php?title=Clockwork_Cult\">the wiki page</a> to learn more.</span>")
return TRUE
return FALSE
/datum/game_mode/clockwork_cult/check_finished()
if(GLOB.ark_of_the_clockwork_justiciar && !GLOB.ratvar_awakens) // Doesn't end until the Ark is destroyed or completed
return FALSE
return ..()
/datum/game_mode/clockwork_cult/proc/check_clockwork_victory()
return main_clockcult.check_clockwork_victory()
/datum/game_mode/clockwork_cult/set_round_result()
..()
if(GLOB.clockwork_gateway_activated)
SSticker.news_report = CLOCK_SUMMON
SSticker.mode_result = "win - servants completed their objective (summon ratvar)"
else
SSticker.news_report = CULT_FAILURE
SSticker.mode_result = "loss - servants failed their objective (summon ratvar)"
/datum/game_mode/clockwork_cult/generate_report()
return "Bluespace monitors near your sector have detected a continuous stream of patterned fluctuations since the station was completed. It is most probable that a powerful entity \
from a very far distance away is using to the station as a vector to cross that distance through bluespace. The theoretical power required for this would be monumental, and if \
the entity is hostile, it would need to rely on a single central power source - disrupting or destroying that power source would be the best way to prevent said entity from causing \
harm to company personnel or property.<br><br>Keep a sharp on any crew that appear to be oddly-dressed or using what appear to be magical powers, as these crew may be defectors \
working for this entity and utilizing highly-advanced technology to cross the great distance at will. If they should turn out to be a credible threat, the task falls on you and \
your crew to dispatch it in a timely manner."
/datum/game_mode/proc/update_servant_icons_added(datum/mind/M)
var/datum/atom_hud/antag/A = GLOB.huds[ANTAG_HUD_CLOCKWORK]
A.join_hud(M.current)
set_antag_hud(M.current, "clockwork")
/datum/game_mode/proc/update_servant_icons_removed(datum/mind/M)
var/datum/atom_hud/antag/A = GLOB.huds[ANTAG_HUD_CLOCKWORK]
A.leave_hud(M.current)
set_antag_hud(M.current, null)
//Servant of Ratvar outfit
/datum/outfit/servant_of_ratvar
name = "Servant of Ratvar"
uniform = /obj/item/clothing/under/rank/engineering/engineer //no more chameleon suit for them, as requested
shoes = /obj/item/clothing/shoes/sneakers/black
back = /obj/item/storage/backpack
ears = /obj/item/radio/headset
gloves = /obj/item/clothing/gloves/color/yellow
belt = /obj/item/storage/belt/utility/servant
backpack_contents = list(/obj/item/storage/box/survival/engineer=1,\
/obj/item/clockwork/replica_fabricator = 1, /obj/item/stack/tile/brass/fifty = 1, /obj/item/reagent_containers/food/drinks/bottle/holyoil = 1)
id = /obj/item/pda
var/plasmaman //We use this to determine if we should activate internals in post_equip()
/datum/outfit/servant_of_ratvar/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
if(H.dna.species.id == "plasmaman") //Plasmamen get additional equipment because of how they work
head = /obj/item/clothing/head/helmet/space/plasmaman
uniform = /obj/item/clothing/under/plasmaman //Plasmamen generally shouldn't need chameleon suits anyways, since everyone expects them to wear their fire suit
r_hand = /obj/item/tank/internals/plasmaman/belt/full
mask = /obj/item/clothing/mask/breath
plasmaman = TRUE
/datum/outfit/servant_of_ratvar/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
var/obj/item/card/id/W = new(H)
var/obj/item/pda/PDA = H.wear_id
W.assignment = "Assistant"
W.access += ACCESS_MAINT_TUNNELS
W.registered_name = H.real_name
W.update_label()
if(plasmaman && !visualsOnly) //If we need to breathe from the plasma tank, we should probably start doing that
H.internal = H.get_item_for_held_index(2)
H.update_internals_hud_icon(1)
PDA.owner = H.real_name
PDA.ownjob = "Assistant"
PDA.update_label()
PDA.id_check(H, W)
H.sec_hud_set_ID()