Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full cult gamemode refactor - Foundational work (runes, talismans, arcane tome) #9053

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3ce3470
agony
Mar 5, 2023
1fee53a
a
Mar 5, 2023
cf1e2f1
moar runes
Mar 5, 2023
0724b6f
tome
Mar 6, 2023
ab0f10d
SURELY I'll work on this later
Mar 6, 2023
6b7cd1e
Files for most of the other runes
Mar 6, 2023
779625d
moar runes
Mar 11, 2023
0f2cc41
runes runes runes
Mar 12, 2023
5880084
I think this is everything but the talismans
Mar 12, 2023
4138569
the rest of the runes I think
Mar 12, 2023
51209b5
start work on talismans, make the files
Mar 12, 2023
314d41c
ui and talismans oh my
Mar 12, 2023
d6695da
feature parity?!
Mar 12, 2023
01b1392
update the tgui
Mar 12, 2023
7460a56
Custom tgui style
Mar 12, 2023
8e03c8a
Gut the old stuff - ritual.dm, runes.dm...
Mar 12, 2023
fd60e32
newrune -> rune, newtalisman -> talisman
Mar 12, 2023
ec9144c
Return null rod functionality and make it compile
Mar 12, 2023
5147137
tgui, moar documentation
Mar 12, 2023
e2b5f9a
blood runes actually involve blood + polishing
Mar 13, 2023
9846ddd
Polishing and consistency
Mar 13, 2023
e53ca70
bwahwab
Mar 13, 2023
cc9b122
Weapon rune, interact_message, UI improvements
Mar 14, 2023
6332d7a
Description pass now that we're close to done
Mar 14, 2023
513cef5
isconstruct() helper, mostly for future use
Mar 14, 2023
8eb46f4
Proofreading, extra spoops
Mar 14, 2023
ac55899
Start work on nanoui iteration at the request of ater
Mar 20, 2023
0f8bc30
Remove arcane tome TGUI stuff
Mar 20, 2023
e04aa5f
Hopefully resolve linter err
Mar 20, 2023
e914ae4
fixes and such
Mar 20, 2023
75b0b38
Use a hook for mapgen runes
Apr 1, 2023
da2fc56
Merge branch 'master' of https://github.com/PolarisSS13/Polaris into …
Ilysen Jul 25, 2023
5eaa6ff
Reapply robe icons
Ilysen Jul 25, 2023
f90c891
Variable invoker texts
Ilysen Jul 25, 2023
1250369
Description tweaks and runtime fixes
Ilysen Jul 25, 2023
83398d8
special name and desc for debug tome
Ilysen Jul 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions code/__defines/gamemode.dm
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,35 @@ var/global/list/be_special_flags = list(

/*
Changeling Defines
*/
*/
#define CHANGELING_POWER_INHERENT "Inherent"
#define CHANGELING_POWER_ARMOR "Armor"
#define CHANGELING_POWER_STINGS "Stings"
#define CHANGELING_POWER_SHRIEKS "Shrieks"
#define CHANGELING_POWER_HEALTH "Health"
#define CHANGELING_POWER_ENHANCEMENTS "Enhancements"
#define CHANGELING_POWER_WEAPONS "Weapons"
#define CHANGELING_POWER_WEAPONS "Weapons"

// English cult words. Each one should represent a concept.
#define CULT_WORD_BLOOD "blood"
#define CULT_WORD_DESTROY "destroy"
#define CULT_WORD_HELL "hell"
#define CULT_WORD_HIDE "hide"
#define CULT_WORD_JOIN "join"
#define CULT_WORD_OTHER "other"
#define CULT_WORD_SELF "self"
#define CULT_WORD_SEE "see"
#define CULT_WORD_TECHNOLOGY "technology"
#define CULT_WORD_TRAVEL "travel"

// Culty cult words. These are gibberish and can basically be whatever.
#define CULT_WORD_BALAQ "balaq"
#define CULT_WORD_CERTUM "certum"
#define CULT_WORD_EGO "ego"
#define CULT_WORD_GEERI "geeri"
#define CULT_WORD_IRE "ire"
#define CULT_WORD_KARAZET "karazet"
#define CULT_WORD_JATKAA "jatkaa"
#define CULT_WORD_MGAR "mgar"
#define CULT_WORD_NAHLIZET "nahlizet"
#define CULT_WORD_VERI "veri"
2 changes: 2 additions & 0 deletions code/__defines/is_helpers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@

#define isbot(A) istype(A, /mob/living/bot)

#define isconstruct(A) istype(A, /mob/living/simple_mob/construct)

#define ismecha(A) istype(A, /obj/mecha)
#define isvehicle(A) istype(A, /obj/vehicle)

Expand Down
178 changes: 178 additions & 0 deletions code/game/gamemodes/cult/arcane_tome.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#define TAB_ARCHIVES 1
#define TAB_RUNES 2

/// Arcane tomes are the quintessential cultist "tool" and are used to draw runes, smack things with, and other such things.
/// The interface they open (arcane_tome.tmpl) also contains a lot of in-game documentation about how the antagonist works.
/obj/item/arcane_tome
name = "arcane tome"
desc = "An old, dusty tome with frayed edges and a sinister-looking cover."
icon = 'icons/obj/weapons.dmi'
item_icons = list(
icon_l_hand = 'icons/mob/items/lefthand_books.dmi',
icon_r_hand = 'icons/mob/items/righthand_books.dmi',
)
icon_state = "tome"
item_state = "tome"
throw_speed = 1
throw_range = 5
w_class = ITEMSIZE_SMALL
drop_sound = 'sound/items/drop/book.ogg'
pickup_sound = 'sound/items/pickup/book.ogg'

/// Whether or not a rune is already being written with this tome.
var/scribing = FALSE
/// How long it takes to draw a rune using this tome. Non-positive values are instant.
var/scribe_speed = 5 SECONDS
/// This tome's selected UI tab.
var/tab = TAB_ARCHIVES
/// The selected archive entry in the UI.
var/archive = 0
/// If TRUE, the rune list in the tome's UI will be displayed in a more concise way.
var/compact_mode = FALSE

/obj/item/arcane_tome/get_examine_desc()
if (iscultist(usr) || isobserver(usr))
return "The scriptures of Nar-Sie, The One Who Sees, The Geometer of Blood. Contains the details of every ritual its followers could think of."
else
return desc

/obj/item/arcane_tome/attack_self(mob/user)
if (!iscultist(user))
to_chat(user, "The book is full of illegible scribbles and crudely-drawn shapes. Is this a joke...?")
return
ui_interact(user)

/obj/item/arcane_tome/ui_interact(mob/user, ui_key, datum/nanoui/ui, force_open, master_ui, datum/topic_state/state)
if (!iscultist(user))
return
var/list/data = build_ui_data()
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open = force_open)
if (!ui)
archive = 0
data["archive"] = archive
ui = new(user, src, ui_key, "arcane_tome.tmpl", "Arcane Tome", 500, 600)
ui.set_initial_data(data)
ui.open()
playsound(user, 'sound/bureaucracy/bookopen.ogg', 25, TRUE)

/obj/item/arcane_tome/proc/build_ui_data()
var/dat[0]
var/runes[0]
for (var/V in subtypesof(/obj/effect/rune))
var/obj/effect/rune/NR = V
if (!initial(NR.can_write))
continue
var/obj/item/paper/talisman/T = initial(NR.talisman_path)
var/req_invokers = initial(NR.required_invokers)
var/invokers_text = initial(NR.invokers_text)
runes[++runes.len] = list(
"name" = initial(NR.rune_name),
"invoker_data" = invokers_text != null ? invokers_text : (req_invokers > 1 ? req_invokers : null),
"talisman" = T ? initial(T.tome_desc) : null,
"shorthand" = initial(NR.rune_shorthand) ? initial(NR.rune_shorthand) : initial(NR.rune_desc),
"typepath" = NR
)
dat["runes"] = runes
dat["tab"] = tab
dat["archive"] = archive
dat["compact_mode"] = compact_mode
return dat

/obj/item/arcane_tome/Topic(href, href_list, datum/topic_state/state)
if (..() || !iscultist(usr))
return TRUE
if (href_list["switch_tab"])
tab = text2num(href_list["switch_tab"])
playsound(src, "pageturn", 25, TRUE)
if (href_list["switch_archive"])
archive = text2num(href_list["switch_archive"])
playsound(src, "pageturn", 25, TRUE)
if (href_list["compact_mode"])
compact_mode = !compact_mode
if (href_list["write_rune"])
var/obj/effect/rune/R = text2path(href_list["write_rune"])
if (ispath(R, /obj/effect/rune))
scribe_rune(usr, R)
SSnanoui.update_uis(src)

/obj/item/arcane_tome/attack(mob/living/M, mob/living/user, target_zone, attack_modifier)
// This is basically a reimplementation of weapon logic for cultists only
// It is far from ideal, but it's what we got. Having it always do damage but only letting cultists attack with it
// would mean that it could be used to smash lights and windows etc, and we only want it to work on living things
if (iscultist(user) && !iscultist(M) && user.a_intent == I_HURT)
user.break_cloak()
user.setClickCooldown(user.get_attack_speed(src))
user.do_attack_animation(M)
var/hit_zone = M.resolve_item_attack(src, user, target_zone)
if (hit_zone)
M.interact_message(user,
SPAN_DANGER("\The [user] bathes \the [M] in red light from \the [src]!"),
SPAN_DANGER("\The [user] bathes you in burning red light from \the [src]!"),
SPAN_DANGER("You burn \the [M] with \the [src]!")
)
playsound(src, 'sound/weapons/sear.ogg', 50, TRUE, -1)
M.apply_damage(rand(5, 20), BURN, hit_zone, used_weapon = "internal burns")
return
. = ..()

/// Causes `user` to attempt to create a rune of type `rune_type`, using their blood (or equivalent) as the medium.
/// Inflicts a small amount of damage to the hands and creates blood decals in the process that remain if interrupted.
/obj/item/arcane_tome/proc/scribe_rune(mob/living/user, obj/effect/rune_type)
if (locate(/obj/effect/rune) in get_turf(user))
to_chat(user, SPAN_WARNING("You can only fit one rune on any given space."))
return
if (scribing)
to_chat(user, SPAN_WARNING("You can only scribe one rune at a time."))
return
var/datum/gender/G = gender_datums[user.get_visible_gender()]
var/blood_name = "blood"
var/synth = user.isSynthetic()
if (ishuman(user))
var/mob/living/carbon/human/H = user
blood_name = H.species?.get_blood_name()
user.apply_damage(1, BRUTE, pick(BP_L_HAND, BP_R_HAND), sharp = TRUE, edge = TRUE, used_weapon = "long, precise cut")
user.visible_message(
SPAN_WARNING("\The [user] [!synth ? "slices open [G.his] skin" : "tears open [G.his] circulation"] and begins painting on symbols on the floor with [G.his] own [blood_name]!"),
SPAN_NOTICE("You [!synth ? "slice open your skin" : "tear open your circulation"] and begin drawing a rune on the floor whilst invoking the ritual that binds your life essence with the dark arcane energies flowing through the surrounding world."),
SPAN_WARNING("You hear droplets softly spattering onto the ground."),
range = 3
)
scribing = TRUE
if (ishuman(user) && !max(0, scribe_speed)) // Only drip blood if it actually takes time to write the rune
var/mob/living/carbon/human/H = user
for (var/i in 1 to 4)
spawn (max(0, scribe_speed - 1 SECOND) / i)
H.drip(1)
if (!do_after(user, max(0, scribe_speed)))
to_chat(user, SPAN_WARNING("The ritual is interrupted. Your [blood_name] splatters onto the ground."))
scribing = FALSE
return
scribing = FALSE
if (locate(/obj/effect/rune) in get_turf(user))
to_chat(user, SPAN_WARNING("You can only fit one rune on any given space."))
return
for (var/obj/effect/decal/cleanable/blood/B in get_turf(user))
qdel(B)
user.visible_message(
SPAN_WARNING("\The [user] paints arcane markings with [G.his] own [blood_name]!"),
SPAN_NOTICE("You finish drawing the arcane markings of the Geometer."),
range = 3
)
var/obj/effect/rune/NR = new rune_type (get_turf(user))
NR.after_scribe(user)

/// Debug tome that automatically converts on pickup. Should never appear regularly.
/obj/item/arcane_tome/debug
name = "debug tome"
scribe_speed = 1 SECOND // Not quite instant, but close to it

/obj/item/arcane_tome/debug/get_examine_desc()
return SPAN_DANGER("This should never appear in normal play - report this on the issue tracker if you see it in a normal round!")

/obj/item/arcane_tome/debug/pickup(mob/living/user)
. = ..()
if (user.mind)
cult.add_antagonist(user.mind)

#undef TAB_ARCHIVES
#undef TAB_RUNES
55 changes: 29 additions & 26 deletions code/game/gamemodes/cult/cult_items.dm
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/obj/item/melee/cultblade
name = "cult blade"
desc = "An arcane weapon wielded by the followers of an unknown god."
desc = "A wicked sword made of serrated black metal, with a very slightly curving blade evoking a khopesh. The grip is studded with dull spikes."
description_antag = "Through some anomalous means, this weapon refuses to be wielded by any other than the followers of Nar-Sie. Attempting to wield it without being a cultist yourself is like to result in disaster."
icon_state = "cultblade"
origin_tech = list(TECH_COMBAT = 1, TECH_ARCANE = 1)
w_class = ITEMSIZE_LARGE
Expand All @@ -10,48 +11,50 @@
drop_sound = 'sound/items/drop/sword.ogg'
pickup_sound = 'sound/items/pickup/sword.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
edge = 1
sharp = 1
edge = TRUE
sharp = TRUE

/obj/item/melee/cultblade/cultify()
return

/obj/item/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone)
if(iscultist(user) && !istype(user, /mob/living/simple_mob/construct))
/obj/item/melee/cultblade/attack(mob/living/M, mob/living/user, target_zone)
if (iscultist(user) && !isconstruct(user))
return ..()

var/zone = (user.hand ? "l_arm":"r_arm")
if(ishuman(user))
var/zone = (user.hand ? BP_L_ARM : BP_R_ARM)
if (ishuman(user))
var/mob/living/carbon/human/H = user
var/obj/item/organ/external/affecting = H.get_organ(zone)
to_chat(user, "<span class='danger'>An inexplicable force rips through your [affecting.name], tearing the sword from your grasp!</span>")
//random amount of damage between half of the blade's force and the full force of the blade.
user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp=1, edge=1)
to_chat(user, SPAN_DANGER("An inexplicable force rips through your [affecting.name], tearing \the [src] from your grip!"))
user.apply_damage(rand(force / 2, force), damtype, zone, 0, sharp = sharp, edge = edge)
user.Weaken(5)
else if(!istype(user, /mob/living/simple_mob/construct))
to_chat(user, "<span class='danger'>An inexplicable force rips through you, tearing the sword from your grasp!</span>")
else if (isconstruct(user))
to_chat(user, SPAN_WARNING("The blade hisses, forcing itself from your manipulators. \The [src] will only allow mortals to wield it against foes, not kin."))
else
to_chat(user, "<span class='critical'>The blade hisses, forcing itself from your manipulators. \The [src] will only allow mortals to wield it against foes, not kin.</span>")
to_chat(user, SPAN_DANGER("An inexplicable force rips through you, tearing the sword from your grip!"))

user.drop_from_inventory(src, src.loc)
throw_at(get_edge_target_turf(src, pick(alldirs)), rand(1,3), throw_speed)
user.drop_from_inventory(src, get_turf(user))
throw_at(get_edge_target_turf(src, pick(alldirs)), rand(1, 3), throw_speed)

var/spooky = pick('sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg', 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/wail.ogg')
playsound(src, spooky, 50, 1)
playsound(src, spooky, 50, TRUE)

return 1
return TRUE

/obj/item/melee/cultblade/pickup(mob/living/user as mob)
if(!iscultist(user) && !istype(user, /mob/living/simple_mob/construct))
to_chat(user, "<span class='warning'>An overwhelming feeling of dread comes over you as you pick up the cultist's sword. It would be wise to be rid of this blade quickly.</span>")
/obj/item/melee/cultblade/pickup(mob/living/user)
. = ..()
if (!iscultist(user))
to_chat(user, SPAN_WARNING("An overwhelming feeling of dread comes over you as you pick up \the [src]. It would be wise to be rid of this blade quickly."))
user.make_dizzy(120)
if(istype(user, /mob/living/simple_mob/construct))
to_chat(user, "<span class='warning'>\The [src] hisses, as it is discontent with your acquisition of it. It would be wise to return it to a worthy mortal quickly.</span>")
else if (isconstruct(user))
to_chat(user, SPAN_WARNING("\The [src] hisses, as it is discontent with your acquisition of it. It would be wise to return it to a worthy mortal quickly."))


// Actual cultists spawn with a hooded variant found in hooded.dm; everything below here is legacy for older maps, and has yet to be gutted
/obj/item/clothing/head/culthood
name = "cult hood"
icon_state = "culthood"
desc = "A hood worn by the followers of an unknown god."
desc = "Chips of wet magmellite and strips of leathery textile are melded together into stiff cloth. Its surface is warm and shudders at the touch."
origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1)
flags_inv = HIDEFACE
body_parts_covered = HEAD
Expand All @@ -75,11 +78,11 @@

/obj/item/clothing/suit/cultrobes
name = "cult robes"
desc = "A set of armored robes worn by the followers of an unknown god."
desc = "Chips of wet magmellite and strips of leathery textile are melded together into stiff cloth. Its surface is warm and shudders at the touch."
icon_state = "cultrobes"
origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1)
body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
allowed = list(/obj/item/book/tome,/obj/item/melee/cultblade)
allowed = list(/obj/item/arcane_tome,/obj/item/melee/cultblade)
armor = list(melee = 50, bullet = 30, laser = 50, energy = 80, bomb = 25, bio = 10, rad = 0)
flags_inv = HIDEJUMPSUIT
siemens_coefficient = 0
Expand Down Expand Up @@ -114,7 +117,7 @@
origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1)
desc = "A bulky suit of armour, bristling with spikes. It looks space-worthy."
w_class = ITEMSIZE_NORMAL
allowed = list(/obj/item/book/tome,/obj/item/melee/cultblade,/obj/item/tank/emergency/oxygen,/obj/item/suit_cooling_unit)
allowed = list(/obj/item/arcane_tome,/obj/item/melee/cultblade,/obj/item/tank/emergency/oxygen,/obj/item/suit_cooling_unit)
slowdown = 0.5
armor = list(melee = 60, bullet = 50, laser = 30, energy = 80, bomb = 30, bio = 30, rad = 30)
siemens_coefficient = 0
Expand Down
2 changes: 1 addition & 1 deletion code/game/gamemodes/cult/cultify/mob.dm
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
G.invisibility = 0
to_chat(G, "<span class='sinister'>You feel relieved as what's left of your soul finally escapes its prison of flesh.</span>")

cult.harvested += G.mind
LAZYADD(cult.harvested, G.mind)
else
dust()

Expand Down
2 changes: 1 addition & 1 deletion code/game/gamemodes/cult/cultify/obj.dm
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
return

/obj/item/book/cultify()
new /obj/item/book/tome(loc)
new /obj/item/arcane_tome(loc)
..()

/obj/item/material/sword/cultify()
Expand Down
2 changes: 0 additions & 2 deletions code/game/gamemodes/cult/hell_universe.dm
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ In short:
OverlayAndAmbientSet()
lightsout(0,0)

runedec += 9000 //basically removing the rune cap


/datum/universal_state/hell/proc/AreaSet()
for(var/area/A in world)
Expand Down
Loading