diff --git a/code/controllers/subsystem/achievements.dm b/code/controllers/subsystem/achievements.dm
index fcbae35b3515..2119f9c2e20c 100644
--- a/code/controllers/subsystem/achievements.dm
+++ b/code/controllers/subsystem/achievements.dm
@@ -52,6 +52,8 @@ SUBSYSTEM_DEF(achievements)
if(!achievement)
log_sql("Achievement [achievementPath] not found in list of achievements when trying to unlock for [C.ckey]")
return FALSE
+ if(istype(achievement,/datum/achievement/greentext) && achievementPath != /datum/achievement/greentext)
+ unlock_achievement(/datum/achievement/greentext,C) // Oooh, a little bit recursive!
if(!has_achievement(achievementPath, C))
var/datum/DBQuery/medalQuery = SSdbcore.NewQuery("INSERT INTO [format_table_name("earned_achievements")] (ckey, id) VALUES ('[C.ckey]', '[achievement.id]')")
medalQuery.Execute()
@@ -90,6 +92,6 @@ SUBSYSTEM_DEF(achievements)
/datum/controller/subsystem/achievements/proc/get_achievement(achievementPath)
for(var/datum/achievement/i in achievements)
- if(istype(i, achievementPath))
+ if(i.type == achievementPath) // Can't use istype() here since it needs to be the EXACT correct type.
return i
return FALSE
diff --git a/code/datums/achievements/achievements.dm b/code/datums/achievements/achievements.dm
index b3d2d2c3e213..26b13ea155ef 100644
--- a/code/datums/achievements/achievements.dm
+++ b/code/datums/achievements/achievements.dm
@@ -1,3 +1,9 @@
+//OFFSETS - Used so that each general like "group" of achievements can be added to w/o fucking up the whole incremental pattern we got going on.
+//DO NOT MAKE OFFSET VALUES THAT ARE GREATER THAN 2^15 OR LESS THAN 128.
+//TO BE HONEST THIS OFFSET DOESN'T EVEN NEED TO BE POWER OF TWO, THOUGH.
+#define GREENTEXT 256 // An offset for new greentext-related achievements, to keep the incremental pattern.
+#define REDTEXT 512 // Offset for redtexts.
+
/datum/achievement
var/name = "achievement"
var/desc = "Please make an issue on github, including this achievement's name and how you got it."
@@ -30,7 +36,7 @@
desc = "As a member of the Crew, deal a Humiliating defeat to Nuclear Team"
id = 5
-/datum/achievement/nukewin
+/datum/achievement/greentext/nukewin
name = "Delta Alert"
desc = "As a Nuclear Operative, score a Major or Minor Victory"
id = 6
@@ -52,7 +58,7 @@
id = 9
hidden = TRUE
-/datum/achievement/wizwin
+/datum/achievement/greentext/wizwin
name = "Scholars of the Arcane"
desc = "As a wizard, complete your objectives"
id = 10
@@ -68,12 +74,12 @@
id = 12
hidden = TRUE
-/datum/achievement/changelingwin
+/datum/achievement/greentext/changelingwin
name = "The Thing"
desc = "As a changeling, complete your objectives"
id = 13
-/datum/achievement/slingascend
+/datum/achievement/greentext/slingascend
name = "The Dark Shadow"
desc = "As a shadowling, ascend successfully"
id = 14
@@ -131,3 +137,108 @@
name = "Duke of Ducats"
desc = "As the QM, have a million cargo credits by the end of the round" //Cargoking-junior
id = 22
+
+// The achievements that are basically just "greentext as this sort of antag"
+
+/datum/achievement/greentext
+ name = "Green Text"
+ desc = "As an Antagonist achieve your first green text"
+ id = GREENTEXT + 1
+
+/datum/achievement/greentext/ratvar
+ name = "Clock Work"
+ desc = "As a Servant of Ratvar summon Ratvar"
+ id = GREENTEXT + 2
+
+/datum/achievement/greentext/ratvar/eminence
+ name = "Clock Work"
+ desc = "As the Eminence, summon Ratvar"
+ id = GREENTEXT + 3
+
+/datum/achievement/greentext/narsie
+ name = "Blood Rites"
+ desc = "As a member of Blood Cult summon Nar-Sie"
+ id = GREENTEXT + 4
+
+/datum/achievement/greentext/narsie/master
+ name = "Master of Blood"
+ desc = "As a Cult Master, summon Nar-Sie"
+ id = GREENTEXT + 5
+
+/datum/achievement/greentext/revolution
+ name = "Down with Nanotrasen"
+ desc = "As a Revolutionary, complete your objectives"
+ id = GREENTEXT + 6
+
+/datum/achievement/greentext/revolution/head
+ name = "Viva la Revolution!"
+ desc = "As a Head Revolutionary, complete your objectives"
+ id = GREENTEXT + 7
+
+/datum/achievement/greentext/gang
+ name = "Turf War"
+ desc = "As a Gang Member, take over the station"
+ id = GREENTEXT + 8
+
+/datum/achievement/greentext/gangleader
+ name = "\"I have built my organization upon fear.\""
+ desc = "As a Gang Leader, take over the station"
+ id = GREENTEXT + 9
+
+/datum/achievement/greentext/blob
+ name = "Grey Goo"
+ desc = "As a Blob complete your objectives"
+ id = GREENTEXT + 10
+
+/datum/achievement/greentext/clownop
+ name = "\"You wouldn't get it\""
+ desc = "As a Clown Operative score a Major or Minor Victory"
+ id = GREENTEXT + 11
+
+/datum/achievement/greentext/internal
+ name = "Triple Cross"
+ desc = "As an Internal Affairs Agent, complete your objectives"
+ id = GREENTEXT + 12
+
+/datum/achievement/greentext/external
+ name = "Quadruple Cross"
+ desc = "As an External Affairs Agent, complete your objectives"
+ id = GREENTEXT + 13
+
+/datum/achievement/greentext/disease
+ name = "Space Aids"
+ desc = "As Sentient Disease, survive and complete your objectives"
+ id = GREENTEXT + 14
+
+/datum/achievement/greentext/pirate
+ name = "Yaaaahr!"
+ desc = "As member of the Pirate crew, collect sufficient bounty from the crew"
+ id = GREENTEXT + 15
+
+/datum/achievement/greentext/vampire
+ name = "Count de Ville"
+ desc = "As a Vampire, complete your objectives"
+ id = GREENTEXT + 16
+
+/datum/achievement/greentext/revenant
+ name = "From The Shadows"
+ desc = "As a Revenant, complete your objectives"
+ id = GREENTEXT + 17
+
+//end-greentext
+
+//start-redtext
+/datum/achievement/redtext
+ name = "Mission Failed, We'll Get'em Next Time"
+ desc = "As an antagonist, fail your objectives."
+ id = REDTEXT + 1
+
+/datum/achievement/redtext/winlost
+ name = "Arcane Failure"
+ desc = "As a Wizard, fail your objectives."
+ id = REDTEXT + 2
+ hidden = TRUE
+//end-redtext
+
+#undef GREENTEXT
+#undef REDTEXT
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 4b862b512487..1733ba5ea9c5 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -20,6 +20,7 @@ GLOBAL_LIST_EMPTY(antagonists)
var/show_in_antagpanel = TRUE //This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
var/antagpanel_category = "Uncategorized" //Antagpanel will display these together, REQUIRED
var/show_name_in_check_antagonists = FALSE //Will append antagonist name in admin listings - use for categories that share more than one antag type
+ var/datum/achievement/greentext/greentext_achieve // The achievement received for greentexting as this antag type. Not all antag types use this to distribute their achievements.
/datum/antagonist/New()
GLOB.antagonists += src
@@ -142,6 +143,10 @@ GLOBAL_LIST_EMPTY(antagonists)
if(objectives.len == 0 || objectives_complete)
report += "The [name] was successful!"
+ if(istype(greentext_achieve))
+ SSachievements.unlock_achievement(greentext_achieve,owner.current)
+ else // The above still does award the generic greentext achievement, just implicitly.
+ SSachievements.unlock_achievement(/datum/achievement/greentext,owner.current.client)
else
report += "The [name] has failed!"
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index f11ca41b9388..edb187e860c3 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -146,6 +146,7 @@
for(var/datum/objective/O in objectives)
if(!O.check_completion())
won = FALSE
+ break
if(won)
result += "[name] team fulfilled its mission!"
else
diff --git a/code/modules/antagonists/blob/blob.dm b/code/modules/antagonists/blob/blob.dm
index 3d6db983dc42..f878e1bf7715 100644
--- a/code/modules/antagonists/blob/blob.dm
+++ b/code/modules/antagonists/blob/blob.dm
@@ -16,6 +16,8 @@
if(!overmind.victory_in_progress) //if it won this doesn't really matter
var/point_report = "
[owner.name] took over [overmind.max_count] tiles at the height of its growth."
return basic_report+point_report
+ else
+ SSachievements.unlock_achievement(/datum/achievement/greentext/blob,overmind.client)
return basic_report
/datum/antagonist/blob/greet()
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index 6bfe77a78901..f4b51889c221 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -575,7 +575,7 @@
if(changelingwin)
parts += "The changeling was successful!"
- SSachievements.unlock_achievement(/datum/achievement/changelingwin, owner.current.client) //changeling wins, give achivement
+ SSachievements.unlock_achievement(/datum/achievement/greentext/changelingwin, owner.current.client) //changeling wins, give achivement
else
parts += "The changeling has failed."
diff --git a/code/modules/antagonists/clockcult/clockcult.dm b/code/modules/antagonists/clockcult/clockcult.dm
index 57297bdc7337..ac6b374fbf5d 100644
--- a/code/modules/antagonists/clockcult/clockcult.dm
+++ b/code/modules/antagonists/clockcult/clockcult.dm
@@ -213,6 +213,10 @@
if(check_clockwork_victory())
parts += "Ratvar's servants defended the Ark until its activation!"
+ for(var/mind in SSticker.mode.servants_of_ratvar)
+ var/datum/mind/M = mind
+ SSachievements.unlock_achievement(/datum/achievement/greentext/ratvar,M.current.client)
+ SSachievements.unlock_achievement(/datum/achievement/greentext/ratvar/eminence,eminence.current.client)
else
parts += "The Ark was destroyed! Ratvar will rust away for all eternity!"
parts += " "
diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm
index f21389261f62..2f142663e41b 100644
--- a/code/modules/antagonists/cult/cult.dm
+++ b/code/modules/antagonists/cult/cult.dm
@@ -380,6 +380,11 @@
if(check_cult_victory())
parts += "The cult has succeeded! Nar-sie has snuffed out another torch in the void!"
+ for(var/mind in members)
+ var/datum/mind/M = mind
+ SSachievements.unlock_achievement(/datum/achievement/greentext/narsie,M.current.client)
+ if(M.has_antag_datum(/datum/antagonist/cult/master))
+ SSachievements.unlock_achievement(/datum/achievement/greentext/narsie/master,M.current.client)
else
parts += "The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!"
diff --git a/code/modules/antagonists/disease/disease_datum.dm b/code/modules/antagonists/disease/disease_datum.dm
index 1aef9ceba6db..f55b5c8d9b79 100644
--- a/code/modules/antagonists/disease/disease_datum.dm
+++ b/code/modules/antagonists/disease/disease_datum.dm
@@ -57,6 +57,8 @@
if(win)
result += "The [special_role_text] was successful!"
+ if(istype(owner.current, /mob/camera/disease))
+ SSachievements.unlock_achievement(/datum/achievement/greentext/disease,owner.current.client)
else
result += "The [special_role_text] has failed!"
diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm
index 13c1702a3d35..6dcd2122ec38 100644
--- a/code/modules/antagonists/nukeop/nukeop.dm
+++ b/code/modules/antagonists/nukeop/nukeop.dm
@@ -380,8 +380,12 @@
SSachievements.unlock_achievement(/datum/achievement/flukeops, H.client)
if(NUKE_RESULT_NUKE_WIN, NUKE_RESULT_DISK_LOST)
for(var/mob/living/carbon/human/H in GLOB.player_list)
- if(is_nuclear_operative(H))
- SSachievements.unlock_achievement(/datum/achievement/nukewin, H.client)
+ var/datum/mind/M = H.mind
+ if(M && M.has_antag_datum(/datum/antagonist/nukeop))
+ if(M.has_antag_datum(/datum/antagonist/nukeop/clownop) || M.has_antag_datum(/datum/antagonist/nukeop/leader/clownop))
+ SSachievements.unlock_achievement(/datum/achievement/greentext/clownop, H.client)
+ else
+ SSachievements.unlock_achievement(/datum/achievement/greentext/nukewin, H.client)
/datum/team/nuclear/antag_listing_name()
diff --git a/code/modules/antagonists/pirate/pirate.dm b/code/modules/antagonists/pirate/pirate.dm
index 754da76e10ac..c3d3acc1bcaa 100644
--- a/code/modules/antagonists/pirate/pirate.dm
+++ b/code/modules/antagonists/pirate/pirate.dm
@@ -101,6 +101,8 @@
if(L.check_completion() && !all_dead)
parts += "The pirate crew was successful!"
+ for(var/datum/mind/M in members)
+ SSachievements.unlock_achievement(/datum/achievement/greentext/pirate,M.current.client)
else
parts += "The pirate crew has failed."
diff --git a/code/modules/antagonists/revenant/revenant_antag.dm b/code/modules/antagonists/revenant/revenant_antag.dm
index 8d99edf26ec6..f5b5aeb8ac1b 100644
--- a/code/modules/antagonists/revenant/revenant_antag.dm
+++ b/code/modules/antagonists/revenant/revenant_antag.dm
@@ -2,6 +2,7 @@
name = "Revenant"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
+ greentext_achieve = /datum/achievement/greentext/revenant
/datum/antagonist/revenant/greet()
owner.announce_objectives()
diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm
index dc2b6cfd1731..ccd4895b3478 100644
--- a/code/modules/antagonists/revolution/revolution.dm
+++ b/code/modules/antagonists/revolution/revolution.dm
@@ -303,6 +303,12 @@
addtimer(CALLBACK(src,.proc/update_heads),HEAD_UPDATE_PERIOD,TIMER_UNIQUE)
+/datum/team/revolution/proc/check_victory()
+ for(var/datum/objective/O in objectives)
+ if(!O.check_completion())
+ return FALSE
+ return TRUE
+
/datum/team/revolution/roundend_report()
if(!members.len)
return
@@ -326,6 +332,13 @@
var/list/targets = list()
var/list/datum/mind/headrevs = get_antag_minds(/datum/antagonist/rev/head)
var/list/datum/mind/revs = get_antag_minds(/datum/antagonist/rev,TRUE)
+ if(check_victory())
+ for(var/H in revs)
+ var/datum/mind/M = H
+ SSachievements.unlock_achievement(/datum/achievement/greentext/revolution,M.current.client)
+ if(M.has_antag_datum(/datum/antagonist/rev/head))
+ SSachievements.unlock_achievement(/datum/achievement/greentext/revolution/head,M.current.client)
+
if(headrevs.len)
var/list/headrev_part = list()
headrev_part += ""
diff --git a/code/modules/antagonists/traitor/IAA/internal_affairs.dm b/code/modules/antagonists/traitor/IAA/internal_affairs.dm
index 32f4370d0fab..c35e97abcda4 100644
--- a/code/modules/antagonists/traitor/IAA/internal_affairs.dm
+++ b/code/modules/antagonists/traitor/IAA/internal_affairs.dm
@@ -12,7 +12,7 @@
var/syndicate = FALSE
var/last_man_standing = FALSE
var/list/datum/mind/targets_stolen
-
+ greentext_achieve = /datum/achievement/greentext/internal
/datum/antagonist/traitor/internal_affairs/proc/give_pinpointer()
if(owner && owner.current)
@@ -234,6 +234,7 @@
special_role = TRAITOR_AGENT_ROLE
syndicate = TRUE
forge_single_objective()
+ greentext_achieve = /datum/achievement/greentext/external
/datum/antagonist/traitor/internal_affairs/forge_traitor_objectives()
forge_iaa_objectives()
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index 252f5e62bafc..d1079c96fe71 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -421,6 +421,9 @@
if(traitorwin)
result += "The [special_role_text] was successful!"
+ SSachievements.unlock_achievement(/datum/achievement/greentext,owner.current.client)
+ if(istype(greentext_achieve))
+ SSachievements.unlock_achievement(greentext_achieve,owner.current)
else
result += "The [special_role_text] has failed!"
SEND_SOUND(owner.current, 'sound/ambience/ambifailure.ogg')
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index 6effc95ebc15..7ae2d0a14f78 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -304,10 +304,10 @@
if(wizardwin)
parts += "The wizard was successful!"
- SSachievements.unlock_achievement(/datum/achievement/wizwin, owner.current.client) //wizard wins, give achievement
+ SSachievements.unlock_achievement(/datum/achievement/greentext/wizwin, owner.current.client) //wizard wins, give achievement
else
parts += "The wizard has failed!"
-
+ SSachievements.unlock_achievement(/datum/achievement/redtext/winlost, owner.current.client) //wizard loses, still give achievement lol
if(owner.spell_list.len>0)
parts += "[owner.name] used the following spells: "
var/list/spell_names = list()
diff --git a/yogstation/code/datums/antagonists/vampire.dm b/yogstation/code/datums/antagonists/vampire.dm
index 89f637d9dc3b..997e53a15a8e 100644
--- a/yogstation/code/datums/antagonists/vampire.dm
+++ b/yogstation/code/datums/antagonists/vampire.dm
@@ -341,6 +341,7 @@
if(vampwin)
result += "The vampire was successful!"
+ SSachievements.unlock_achievement(/datum/achievement/greentext/vampire, owner.current.client)
else
result += "The vampire has failed!"
SEND_SOUND(owner.current, 'sound/ambience/ambifailure.ogg')
diff --git a/yogstation/code/modules/antagonists/gang/gang.dm b/yogstation/code/modules/antagonists/gang/gang.dm
index 777af61dd83d..b10e74b47034 100644
--- a/yogstation/code/modules/antagonists/gang/gang.dm
+++ b/yogstation/code/modules/antagonists/gang/gang.dm
@@ -331,6 +331,10 @@
report += ""
if(winner)
report += "The [name] gang was successful!"
+ for(var/datum/mind/M in leaders)
+ SSachievements.unlock_achievement(/datum/achievement/greentext/gangleader,M.current)
+ for(var/datum/mind/M in members) // Leaders are included in this too
+ SSachievements.unlock_achievement(/datum/achievement/greentext/gang,M.current) // and so get the lower achievement, too
else
report += "The [name] gang has failed!"
diff --git a/yogstation/code/modules/antagonists/shadowling/special_shadowling_abilities.dm b/yogstation/code/modules/antagonists/shadowling/special_shadowling_abilities.dm
index 0fcbb44f378f..0de60f91815f 100644
--- a/yogstation/code/modules/antagonists/shadowling/special_shadowling_abilities.dm
+++ b/yogstation/code/modules/antagonists/shadowling/special_shadowling_abilities.dm
@@ -164,7 +164,7 @@
SEND_SOUND(M, sound('sound/hallucinations/veryfar_noise.ogg'))
for(var/obj/machinery/power/apc/A in GLOB.apcs_list)
A.overload_lighting()
- SSachievements.unlock_achievement(/datum/achievement/slingascend, H.client)
+ SSachievements.unlock_achievement(/datum/achievement/greentext/slingascend, H.client)
var/mob/A = new /mob/living/simple_animal/ascendant_shadowling(H.loc)
for(var/X in H.mind.spell_list)
var/obj/effect/proc_holder/spell/S = X