Skip to content

Commit

Permalink
Ports Commends from /tg/ (#3851)
Browse files Browse the repository at this point in the history
## About The Pull Request
ports
tgstation/tgstation#51217
tgstation/tgstation#59543

![dreamseeker_zyNTS6lV41](https://github.com/user-attachments/assets/97cc4da0-4ad3-4f9f-bc7a-81e58bdc6170)

## Why It's Good For The Game
@Imaginos16 asked me to

## Changelog

:cl: Ryll/Shaps
add: Adds a config option for OOC kindness commendations! When enabled,
a small percentage of the crew gets asked if anyone made their round
better, and those people will get a little heart next to their name in
OOC for 24h!
/:cl:
  • Loading branch information
Erikafox authored Jan 3, 2025
1 parent d7fe4fa commit 880cfa2
Show file tree
Hide file tree
Showing 14 changed files with 200 additions and 5 deletions.
98 changes: 98 additions & 0 deletions code/__HELPERS/hearted.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/// Called when the shuttle starts launching back to centcom, polls a few random players who joined the round for commendations
/datum/controller/subsystem/ticker/proc/poll_hearts()
if(!CONFIG_GET(number/commendation_percent_poll))
return
var/number_to_ask = round(LAZYLEN(GLOB.joined_player_list) * CONFIG_GET(number/commendation_percent_poll)) + rand(0,1)

if(number_to_ask == 0)
message_admins("Not enough eligible players to poll for commendations.")
return

message_admins("Polling [number_to_ask] players for commendations.")

for(var/i in GLOB.joined_player_list)
var/mob/check_mob = get_mob_by_ckey(i)
if(!check_mob?.mind || !check_mob.client)
continue
// maybe some other filters like bans or whatever
INVOKE_ASYNC(check_mob, TYPE_PROC_REF(/mob, query_heart), 1)
number_to_ask--
if(number_to_ask <= 0)
break

/// Once the round is actually over, cycle through the ckeys in the hearts list and give them the hearted status
/datum/controller/subsystem/ticker/proc/handle_hearts()
var/list/message = list("The following players were commended this round: ")
var/i = 0
for(var/hearted_ckey in hearts)
i++
var/mob/hearted_mob = get_mob_by_ckey(hearted_ckey)
if(!hearted_mob?.client)
continue
hearted_mob.client.adjust_heart()
message += "[hearted_ckey][i==hearts.len ? "" : ", "]"
message_admins(message.Join())

/// Ask someone if they'd like to award a commendation for the round, 3 tries to get the name they want before we give up
/mob/proc/query_heart(attempt=1)
if(!client || attempt > 3)
return
if(attempt == 1 && tgui_alert(src, "Was there another character you noticed being kind this round that you would like to anonymously thank?", "<3?", list("Yes", "No"), timeout = 30 SECONDS) != "Yes")
return

var/heart_nominee
switch(attempt)
if(1)
heart_nominee = input(src, "What was their name? Just a first or last name may be enough. (Leave blank to cancel)", "<3?")
if(2)
heart_nominee = input(src, "Try again, what was their name? Just a first or last name may be enough. (Leave blank to cancel)", "<3?")
if(3)
heart_nominee = input(src, "One more try, what was their name? Just a first or last name may be enough. (Leave blank to cancel)", "<3?")
if(!heart_nominee)
return

heart_nominee = lowertext(heart_nominee)
var/list/name_checks = get_mob_by_name(heart_nominee)
if(!name_checks || name_checks.len == 0)
query_heart(attempt + 1)
return
name_checks = shuffle(name_checks)

for(var/i in name_checks)
var/mob/heart_contender = i
if(heart_contender == src)
continue

switch(tgui_alert(src, "Is this the person: [heart_contender.name] ([heart_contender.real_name])", "<3?", list("Yes!", "Nope", "Cancel"), timeout = 15 SECONDS))
if("Yes!")
heart_contender.receive_heart(src)
return
if("Nope")
continue
else
return

query_heart(attempt + 1)

/*
* Once we've confirmed who we're commending, either set their status now or log it for the end of the round
*
* This used to be reversed, being named nominate_heart and being called on the mob sending the commendation and the first argument being
* the heart_recepient, but that was confusing and unintuitive, so now src is the person being commended and the sender is now the first argument.
*
* Arguments:
* * heart_sender: The reference to the mob who sent the commendation, just for the purposes of logging
* * duration: How long from the moment it's applied the heart will last
* * instant: If TRUE (or if the round is already over), we'll give them the heart status now, if FALSE, we wait until the end of the round (which is the standard behavior)
*/

/mob/proc/receive_heart(mob/heart_sender, duration = 24 HOURS, instant = FALSE)
if(!client)
return
to_chat(heart_sender, span_nicegreen("Commendation sent!"))
message_admins("[key_name(heart_sender)] commended [key_name(src)] [instant ? "(instant)" : ""]")
log_admin("[key_name(heart_sender)] commended [key_name(src)] [instant ? "(instant)" : ""]")
if(instant || SSticker.current_state == GAME_STATE_FINISHED)
client.adjust_heart(duration)
else
LAZYADD(SSticker.hearts, ckey)
5 changes: 5 additions & 0 deletions code/__HELPERS/roundend.dm
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@

CHECK_TICK

//check config blah blah
handle_hearts()

CHECK_TICK

//Now print them all into the log!
log_game("Antagonists at round end were...")
for(var/antag_name in total_antagonists)
Expand Down
3 changes: 3 additions & 0 deletions code/controllers/configuration/entries/game_options.dm
Original file line number Diff line number Diff line change
Expand Up @@ -382,3 +382,6 @@
max_val = 255
config_entry_value = 127
min_val = 127

/datum/config_entry/number/commendation_percent_poll
integer = FALSE
2 changes: 2 additions & 0 deletions code/controllers/subsystem/shuttle.dm
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ SUBSYSTEM_DEF(shuttle)
jump_timer = addtimer(VARSET_CALLBACK(src, jump_mode, BS_JUMP_COMPLETED), jump_completion_time, TIMER_STOPPABLE)
priority_announce("Jump initiated. ETA: [jump_completion_time / (1 MINUTES)] minutes.", null, null, "Priority")

INVOKE_ASYNC(SSticker, TYPE_PROC_REF(/datum/controller/subsystem/ticker,poll_hearts))

/datum/controller/subsystem/shuttle/proc/request_transit_dock(obj/docking_port/mobile/M)
if(!istype(M))
CRASH("[M] is not a mobile docking port")
Expand Down
2 changes: 2 additions & 0 deletions code/controllers/subsystem/ticker.dm
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ SUBSYSTEM_DEF(ticker)
/// Why an emergency shuttle was called
var/emergency_reason

/// People who have been commended and will receive a heart
var/list/hearts

/datum/controller/subsystem/ticker/Initialize(timeofday)
load_mode()
Expand Down
1 change: 1 addition & 0 deletions code/modules/admin/admin.dm
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@
body += "<A href='?_src_=holder;[HrefToken()];tdome2=[REF(M)]'>Thunderdome 2</A>"
body += "<A href='?_src_=holder;[HrefToken()];tdomeadmin=[REF(M)]'>Thunderdome Admin</A>"
body += "<A href='?_src_=holder;[HrefToken()];tdomeobserve=[REF(M)]'>Thunderdome Observer</A>"
body += "<A href='?_src_=holder;[HrefToken()];admincommend=[REF(M)]'>Commend Behavior</A> | "

body += "<br>"
body += "</body></html>"
Expand Down
13 changes: 13 additions & 0 deletions code/modules/admin/topic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -2166,6 +2166,19 @@
var/datum/poll_question/poll = locate(href_list["submitoptionpoll"]) in GLOB.polls
poll_option_parse_href(href_list, poll, option)

else if(href_list["admincommend"])
var/mob/heart_recepient = locate(href_list["admincommend"])
if(!heart_recepient?.ckey)
to_chat(usr, "<span class='warning'>This mob either no longer exists or no longer is being controlled by someone!</span>")
return
switch(tgui_alert(usr, "Would you like the effects to apply immediately or at the end of the round? Applying them now will make it clear it was an admin commendation.", "<3?", list("Apply now", "Apply at round end", "Cancel")))
if("Apply now")
heart_recepient.receive_heart(usr, instant = TRUE)
if("Apply at round end")
heart_recepient.receive_heart(usr)
else
return

else if (href_list["interview"])
if(!check_rights(R_ADMIN))
return
Expand Down
31 changes: 31 additions & 0 deletions code/modules/admin/verbs/adminhelp.dm
Original file line number Diff line number Diff line change
Expand Up @@ -883,3 +883,34 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
return_list[ASAY_LINK_NEW_MESSAGE_INDEX] = jointext(msglist, " ") // without tuples, we must make do!
return_list[ASAY_LINK_PINGED_ADMINS_INDEX] = pinged_admins
return return_list

/proc/get_mob_by_name(msg)
//This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE!
var/list/ignored_words = list("unknown","the","a","an","of","monkey","alien","as", "i")

//explode the input msg into a list
var/list/msglist = splittext(msg, " ")

//who might fit the shoe
var/list/potential_hits = list()

for(var/i in GLOB.mob_list)
var/mob/M = i
var/list/nameWords = list()
if(!M.mind)
continue

for(var/string in splittext(lowertext(M.real_name), " "))
if(!(string in ignored_words))
nameWords += string
for(var/string in splittext(lowertext(M.name), " "))
if(!(string in ignored_words))
nameWords += string

for(var/string in nameWords)
testing("Name word [string]")
if(string in msglist)
potential_hits += M
break

return potential_hits
10 changes: 10 additions & 0 deletions code/modules/client/client_procs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1172,3 +1172,13 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if("Set-Tab")
stat_tab = payload["tab"]
SSstatpanels.immediate_send_stat_data(src)

///Gives someone hearted status for OOC, from behavior commendations
/client/proc/adjust_heart(duration = 24 HOURS)
var/new_duration = world.realtime + duration
if(prefs.hearted_until > new_duration)
return
to_chat(src, "<span class='nicegreen'>Someone awarded you a heart!</span>")
prefs.hearted_until = new_duration
prefs.hearted = TRUE
prefs.save_preferences()
17 changes: 17 additions & 0 deletions code/modules/client/preferences.dm
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,13 @@ GLOBAL_LIST_EMPTY(preferences_datums)
///The outfit we currently want to preview on our character
var/datum/outfit/job/selected_outfit

///Someone thought we were nice! We get a little heart in OOC until we join the server past the below time (we can keep it until the end of the round otherwise)
var/hearted
///
var/hearted_until



/datum/preferences/New(client/C)
parent = C

Expand Down Expand Up @@ -1135,6 +1142,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(unlock_content || check_rights_for(user.client, R_ADMIN) || custom_ooc)
dat += "<b>OOC Color:</b> <span style='border: 1px solid #161616; background-color: [ooccolor ? ooccolor : GLOB.normal_ooc_colour];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=ooccolor;task=input'>Change</a><br>"

if(hearted_until)
dat += "<a href='?_src_=prefs;preference=clear_heart'>Clear OOC Commend Heart</a><br>"

dat += "</td>"

if(user.client.holder)
Expand Down Expand Up @@ -2430,6 +2440,13 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(current_tab == 2)
show_loadout = TRUE

if("clear_heart")
hearted = FALSE
hearted_until = null
to_chat(user, "<span class='notice'>OOC Commendation Heart disabled</span>")
save_preferences()


ShowChoices(user)
return 1

Expand Down
15 changes: 10 additions & 5 deletions code/modules/client/preferences_savefile.dm
Original file line number Diff line number Diff line change
Expand Up @@ -208,22 +208,26 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
READ_FILE(S["pda_color"], pda_color)
READ_FILE(S["whois_visible"], whois_visible)

// Custom hotkeys
READ_FILE(S["key_bindings"], key_bindings)
check_keybindings()

READ_FILE(S["show_credits"], show_credits)

//favorite outfits
READ_FILE(S["favorite_outfits"], favorite_outfits)

var/list/parsed_favs = list()
for(var/typetext in favorite_outfits)
var/datum/outfit/path = text2path(typetext)
if(ispath(path)) //whatever typepath fails this check probably doesn't exist anymore
parsed_favs += path
favorite_outfits = uniqueList(parsed_favs)

// OOC commendations
READ_FILE(S["hearted_until"], hearted_until)
if(hearted_until > world.realtime)
hearted = TRUE

// Custom hotkeys
READ_FILE(S["key_bindings"], key_bindings)
check_keybindings()

//try to fix any outdated data if necessary
if(needs_update >= 0)
var/bacpath = "[path].updatebac" //todo: if the savefile version is higher then the server, check the backup, and give the player a prompt to load the backup
Expand Down Expand Up @@ -352,6 +356,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["key_bindings"], key_bindings)
WRITE_FILE(S["favorite_outfits"], favorite_outfits)
WRITE_FILE(S["whois_visible"], whois_visible)
WRITE_FILE(S["hearted_until"], (hearted_until > world.realtime ? hearted_until : null))
return TRUE

/datum/preferences/proc/load_character(slot)
Expand Down
3 changes: 3 additions & 0 deletions code/modules/client/verbs/ooc.dm
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
keyname = "<font color='[prefs.ooccolor ? prefs.ooccolor : GLOB.normal_ooc_colour]'>[icon2html('icons/member_content.dmi', world, "blag")][keyname]</font>"
if(prefs.custom_ooc)
keyname = "<font color='[prefs.ooccolor ? prefs.ooccolor : GLOB.normal_ooc_colour]'>[keyname]</font>"
if(prefs.hearted)
var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/chat)
keyname = "[sheet.icon_tag("emoji-heart")][keyname]"
//The linkify span classes and linkify=TRUE below make ooc text get clickable chat href links if you pass in something resembling a url
for(var/client/C in GLOB.clients)
if(C.prefs.chat_toggles & CHAT_OOC)
Expand Down
4 changes: 4 additions & 0 deletions config/game_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -516,3 +516,7 @@ BLUESPACE_JUMP_WAIT 12000

## If admins are allowed to use the authentication server as a regular server for testing
AUTH_ADMIN_TESTING

## HEART COMMENDATIONS ###
## Uncomment this if you'd like to enable commendation pollings for this percentage of players near the end of the round (5% suggested)
COMMENDATION_PERCENT_POLL 0.05
1 change: 1 addition & 0 deletions shiptest.dme
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@
#include "code\__HELPERS\game.dm"
#include "code\__HELPERS\generators.dm"
#include "code\__HELPERS\global_lists.dm"
#include "code\__HELPERS\hearted.dm"
#include "code\__HELPERS\heap.dm"
#include "code\__HELPERS\icon_smoothing.dm"
#include "code\__HELPERS\icons.dm"
Expand Down

0 comments on commit 880cfa2

Please sign in to comment.