Skip to content

Commit

Permalink
Adds a way for SLs/SOs/XOs to recommend medals that the CO can then a…
Browse files Browse the repository at this point in the history
…pprove/deny. (#5202)

# About the pull request

See title

# Explain why it's good for the game

Will likely make medals more used, and means the CO doesn't have to
scramble to get medal recommendations in the last 3 minutes.

# Testing Photographs and Procedure
<details>
<summary>Screenshots & Videos</summary>

How to give recommendations:

![image](https://github.com/cmss13-devs/cmss13/assets/5618080/5a6e0892-bd16-4fe5-aec9-0aa452392f08)
OR

![image](https://github.com/cmss13-devs/cmss13/assets/5618080/38476c5c-7b9d-441f-a91b-64dd1acfc184)

The actual panel for COs:

![image](https://github.com/cmss13-devs/cmss13/assets/5618080/f42336df-e54d-49a0-a851-0177e105df8f)


</details>


# Changelog

:cl:
add: SLs, SOs and XOs can now recommend people for medals! Do this by
right-clicking your headset, or using the new button in the object panel
of the Status Panel!
/:cl:
  • Loading branch information
TheGamerdk committed Jan 6, 2024
1 parent 76cb450 commit 4c853a2
Show file tree
Hide file tree
Showing 7 changed files with 486 additions and 7 deletions.
297 changes: 294 additions & 3 deletions code/datums/medal_awards.dm
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

GLOBAL_LIST_EMPTY(medal_awards)
GLOBAL_LIST_EMPTY(jelly_awards)
GLOBAL_LIST_EMPTY(medal_recommendations)

/datum/recipient_awards
var/list/medal_names
Expand Down Expand Up @@ -160,7 +161,99 @@ GLOBAL_LIST_INIT(human_medals, list(MARINE_CONDUCT_MEDAL, MARINE_BRONZE_HEART_ME

return TRUE

/proc/print_medal(mob/living/carbon/human/user, obj/printer)
/proc/give_medal_award_prefilled(medal_location, mob/giving_mob, chosen_recipient, recipient_rank, recipient_ckey, reason, _medal_type)
var/list/recipient_ranks = list()
for(var/datum/data/record/record in GLOB.data_core.general)
var/recipient_name = record.fields["name"]
recipient_ranks[recipient_name] = record.fields["rank"]

if(!chosen_recipient)
return FALSE

// Pick a medal
var/medal_type = _medal_type
if(!medal_type)
return FALSE

// Write a citation
var/citation = strip_html(reason)
if(!citation)
return FALSE

// Get mob information
var/posthumous = TRUE
var/mob/recipient_mob
var/found_other = FALSE

for(var/mob/mob in GLOB.mob_list)
if(mob.real_name == chosen_recipient)
// Recipient: Check if they are dead, and get some info
// We might not get this info if gibbed, so we'd need to refactor again and find another way if we want stats always correct
if(isliving(mob) && mob.stat != DEAD)
posthumous = FALSE
recipient_mob = mob
if(found_other)
break
found_other = TRUE
if(!recipient_mob)
for(var/mob/mob in GLOB.dead_mob_list)
if(mob.real_name == chosen_recipient)
// Recipient: Check if they are dead?, and get some info
// We might not get this info if gibbed, so we'd need to refactor again and find another way if we want stats always correct
if(isliving(mob) && mob.stat != DEAD)
posthumous = FALSE
recipient_mob = mob
break

// Create the recipient_award
if(!GLOB.medal_awards[chosen_recipient])
GLOB.medal_awards[chosen_recipient] = new /datum/recipient_awards()
var/datum/recipient_awards/recipient_award = GLOB.medal_awards[chosen_recipient]
recipient_award.recipient_rank = recipient_rank
recipient_award.recipient_ckey = recipient_ckey
recipient_award.recipient_mob = recipient_mob
recipient_award.giver_mob += giving_mob
recipient_award.medal_names += medal_type
recipient_award.medal_citations += citation
recipient_award.posthumous += posthumous
recipient_award.giver_ckey += giving_mob.ckey

recipient_award.giver_rank += recipient_ranks[giving_mob.real_name] // Currently not used in marine award message
recipient_award.giver_name += giving_mob.real_name // Currently not used in marine award message

// Create an actual medal item
if(medal_location)
var/obj/item/clothing/accessory/medal/medal
switch(medal_type)
if(MARINE_CONDUCT_MEDAL)
medal = new /obj/item/clothing/accessory/medal/bronze/conduct(medal_location)
if(MARINE_BRONZE_HEART_MEDAL)
medal = new /obj/item/clothing/accessory/medal/bronze/heart(medal_location)
if(MARINE_VALOR_MEDAL)
medal = new /obj/item/clothing/accessory/medal/silver/valor(medal_location)
if(MARINE_HEROISM_MEDAL)
medal = new /obj/item/clothing/accessory/medal/gold/heroism(medal_location)
else
return FALSE
medal.recipient_name = chosen_recipient
medal.medal_citation = citation
medal.recipient_rank = recipient_rank
recipient_award.medal_items += medal
else
recipient_award.medal_items += null

// Recipient: Add the medal to the player's stats
if(recipient_ckey)
var/datum/entity/player_entity/recipient_player = setup_player_entity(recipient_ckey)
if(recipient_player)
recipient_player.track_medal_earned(medal_type, recipient_mob, recipient_rank, citation, giving_mob)

// Inform staff of success
message_admins("[key_name_admin(giving_mob)] awarded a <a href='?medals_panel=1'>[medal_type]</a> to [chosen_recipient] for: \'[citation]\'.")

return TRUE

/proc/open_medal_panel(mob/living/carbon/human/user, obj/printer)
var/obj/item/card/id/card = user.wear_id
if(!card)
to_chat(user, SPAN_WARNING("You must have an authenticated ID Card to award medals."))
Expand All @@ -178,8 +271,9 @@ GLOBAL_LIST_INIT(human_medals, list(MARINE_CONDUCT_MEDAL, MARINE_BRONZE_HEART_ME
user.visible_message("ERROR: ID card not registered for [user.real_name] in USCM registry. Potential medal fraud detected.")
return

if(give_medal_award(get_turf(printer)))
user.visible_message(SPAN_NOTICE("[printer] prints a medal."))
GLOB.ic_medals_panel.user_locs[WEAKREF(user)] = WEAKREF(printer)
GLOB.ic_medals_panel.tgui_interact(user)


GLOBAL_LIST_INIT(xeno_medals, list(XENO_SLAUGHTER_MEDAL, XENO_RESILIENCE_MEDAL, XENO_SABOTAGE_MEDAL, XENO_PROLIFERATION_MEDAL, XENO_REJUVENATION_MEDAL))

Expand Down Expand Up @@ -364,3 +458,200 @@ GLOBAL_LIST_INIT(xeno_medals, list(XENO_SLAUGHTER_MEDAL, XENO_RESILIENCE_MEDAL,
message_admins("[key_name_admin(usr)] deleted [recipient_name]'s <a href='?medals_panel=1'>[medal_type]</a> for: \'[citation]\'.")

return TRUE

/datum/medal_recommendation
var/recipient_rank
var/recipient_ckey
var/recipient_name
var/recommended_by_name
var/recommended_by_ckey
var/reason
var/recommended_by_rank


/proc/add_medal_recommendation(mob/recommendation_giver)
// Pick a marine
var/list/possible_recipients = list()
var/list/recipient_ranks = list()
for(var/datum/data/record/record in GLOB.data_core.general)
var/recipient_name = record.fields["name"]
if(recipient_name == recommendation_giver.real_name)
continue
recipient_ranks[recipient_name] = record.fields["rank"]
possible_recipients += recipient_name
var/chosen_recipient = tgui_input_list(recommendation_giver, "Who do you want to recommend a medal for?", "Medal Recommendation", possible_recipients)
if(!chosen_recipient)
return FALSE

// Write a citation
var/reason = strip_html(tgui_input_text(recommendation_giver, "Why does this person deserve a medal?", "Medal Recommendation", null, MAX_PAPER_MESSAGE_LEN, TRUE), MAX_PAPER_MESSAGE_LEN)
if(!reason)
return FALSE

// Get mob information
var/recipient_rank = recipient_ranks[chosen_recipient]
var/recipient_ckey
var/mob/recipient_mob
var/found_other = FALSE

for(var/mob/mob in GLOB.mob_list)
if(mob.real_name == chosen_recipient)
// We might not get this info if gibbed, so we'd need to refactor again and find another way if we want stats always correct
recipient_ckey = mob.persistent_ckey
recipient_mob = mob
if(found_other)
break
found_other = TRUE
if(!recipient_mob)
for(var/mob/mob in GLOB.dead_mob_list)
if(mob.real_name == chosen_recipient)
// Recipient: Check if they are dead?, and get some info
// We might not get this info if gibbed, so we'd need to refactor again and find another way if we want stats always correct
recipient_ckey = mob.persistent_ckey
recipient_mob = mob
break

// Create the recipient_award
var/datum/medal_recommendation/recommendation = new /datum/medal_recommendation()
GLOB.medal_recommendations += recommendation

recommendation.recipient_rank = recipient_rank
recommendation.recipient_ckey = recipient_ckey
recommendation.recipient_name = recipient_mob.real_name
recommendation.recommended_by_name = recommendation_giver.real_name
recommendation.recommended_by_ckey = recommendation_giver.ckey
recommendation.recommended_by_rank = recipient_ranks[recommendation_giver.real_name]


recommendation.reason = reason

return TRUE


GLOBAL_DATUM_INIT(ic_medals_panel, /datum/ic_medal_panel, new)

/datum/ic_medal_panel
var/name = "Medals Panel"
var/list/datum/weakref/user_locs = list()

/datum/ic_medal_panel/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "IcMedalsPanel", "Medals Panel")
ui.open()
ui.set_autoupdate(FALSE)

/datum/ic_medal_panel/ui_state(mob/user)
var/datum/weakref/user_reference = WEAKREF(user)
var/datum/weakref/loc_reference = user_locs[user_reference]
if(istype(loc_reference?.resolve(), /obj/item))
return GLOB.not_incapacitated_and_inventory_state
else
return GLOB.not_incapacitated_and_adjacent_state

/datum/ic_medal_panel/ui_host(mob/user)
. = ..()
var/datum/weakref/user_reference = WEAKREF(user)
var/datum/weakref/loc_reference = user_locs[user_reference]
. = loc_reference?.resolve()

/datum/ic_medal_panel/ui_data(mob/user)
var/list/data = list()
data["recommendations"] = list()

for(var/datum/medal_recommendation/recommendation in GLOB.medal_recommendations)
var/recommendation_list = list()

recommendation_list["rank"] = recommendation.recipient_rank
recommendation_list["name"] = recommendation.recipient_name
recommendation_list["ref"] = REF(recommendation)
recommendation_list["recommender_name"] = recommendation.recommended_by_name
recommendation_list["reason"] = recommendation.reason
recommendation_list["recommender_rank"] = recommendation.recommended_by_rank

data["recommendations"] += list(recommendation_list)
return data

/datum/ic_medal_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
var/mob/living/carbon/human/user = usr
var/obj/item/card/id/card = user.wear_id
if(!card)
to_chat(user, SPAN_WARNING("You must have an authenticated ID Card to award medals."))
return

if(!((card.paygrade in GLOB.co_paygrades) || (card.paygrade in GLOB.highcom_paygrades)))
to_chat(user, SPAN_WARNING("Only a Senior Officer can award medals!"))
return

if(!card.registered_ref)
user.visible_message("ERROR: ID card not registered in USCM registry. Potential medal fraud detected.")
return

var/real_owner_ref = card.registered_ref

if(real_owner_ref != WEAKREF(user))
user.visible_message("ERROR: ID card not registered for [user.real_name] in USCM registry. Potential medal fraud detected.")
return

var/datum/weakref/user_ref = WEAKREF(user)
var/datum/weakref/loc_ref = user_locs[user_ref]
var/atom/actual_loc = loc_ref?.resolve()
if(!actual_loc)
return

switch(action)
if("grant_new_medal")
if(give_medal_award(get_turf(actual_loc)))
actual_loc.visible_message(SPAN_NOTICE("[actual_loc] prints a medal."))
. = TRUE

if("approve_medal")
var/recommendation_ref = params["ref"]
var/medal_type = params["medal_type"]
if(!(medal_type in GLOB.human_medals))
return
var/datum/medal_recommendation/recommendation = locate(recommendation_ref) in GLOB.medal_recommendations
if(!recommendation)
return
if(recommendation.recipient_name == user.real_name)
to_chat(user, SPAN_WARNING("You cannot give medals to yourself!"))
return

var/choice = tgui_alert(user, "Would you like to change the medal text?", "Medal Citation", list("Yes", "No"))
var/medal_citation = recommendation.reason
if(choice == "Yes")
medal_citation = strip_html(tgui_input_text(user, "What should the medal citation read?", "Medal Citation", null, MAX_PAPER_MESSAGE_LEN, TRUE), MAX_PAPER_MESSAGE_LEN)

var/confirm_choice = tgui_alert(user, "Are you sure you want to give a medal to [recommendation.recipient_name]?", "Medal Confirmation", list("Yes", "No"))
if(confirm_choice != "Yes")
return

if(give_medal_award_prefilled(get_turf(actual_loc), user, recommendation.recipient_name, recommendation.recipient_rank, recommendation.recipient_ckey, medal_citation, medal_type, recommendation.recommended_by_ckey, recommendation.recommended_by_name))
GLOB.medal_recommendations -= recommendation
qdel(recommendation)
user.visible_message(SPAN_NOTICE("[actual_loc] prints a medal."))
. = TRUE

if("deny_medal")
var/recommendation_ref = params["ref"]
var/datum/medal_recommendation/recommendation = locate(recommendation_ref) in GLOB.medal_recommendations
if(!recommendation)
return
var/confirm = tgui_alert(user, "Are you sure you want to deny this medal recommendation?", "Medal Confirmation", list("Yes", "No"))
if(confirm != "Yes")
return
GLOB.medal_recommendations -= recommendation
qdel(recommendation)
. = TRUE

/datum/ic_medal_panel/ui_close(mob/user)
. = ..()
user_locs -= WEAKREF(user)

/datum/ic_medal_panel/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet/medal)
)
2 changes: 1 addition & 1 deletion code/game/machinery/computer/almayer_control.dm
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
return
switch(action)
if("award")
print_medal(usr, src)
open_medal_panel(usr, src)
. = TRUE

// evac stuff start \\
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/computer/communications.dm
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@
cooldown_message = world.time

if("award")
print_medal(usr, src)
open_medal_panel(usr, src)

if("evacuation_start")
if(state == STATE_EVACUATION)
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/computer/groundside_operations.dm
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@
log_announcement("[key_name(usr)] has announced the following: [input]")

if("award")
print_medal(usr, src)
open_medal_panel(usr, src)

if("selectlz")
if(SSticker.mode.active_lz)
Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items/devices/cictablet.dm
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@
if("award")
if(announcement_faction != FACTION_MARINE)
return
print_medal(usr, src)
open_medal_panel(usr, src)
. = TRUE

if("mapview")
Expand Down
18 changes: 18 additions & 0 deletions code/game/objects/items/devices/radio/headset.dm
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,24 @@
var/datum/techtree/T = GET_TREE(TREE_MARINE)
T.enter_mob(usr)

/obj/item/device/radio/headset/almayer/verb/give_medal_recommendation()
set name = "Give Medal Recommendation"
set desc = "Send a medal recommendation for approval by the Commanding Officer"
set category = "Object.Medals"
set src in usr

var/mob/living/carbon/human/wearer = usr
if(!istype(wearer))
return
var/obj/item/card/id/id_card = wearer.wear_id?.GetID()
if(!istype(id_card))
return
if(!(id_card.rank in list(JOB_SO, JOB_XO, JOB_SQUAD_LEADER)))
to_chat(wearer, SPAN_WARNING("Only Staff Officers, Executive Officers and Squad Leaders are permitted to give medal recommendations!"))
return
if(add_medal_recommendation(usr))
to_chat(usr, SPAN_NOTICE("Recommendation successfully submitted."))

/obj/item/device/radio/headset/almayer/ce
name = "chief engineer's headset"
desc = "The headset of the guy in charge of spooling engines, managing MTs, and tearing up the floor for scrap metal. Of robust and sturdy construction. Channels are as follows: :n - engineering, :v - marine command, :m - medical, :u - requisitions, :a - alpha squad, :b - bravo squad, :c - charlie squad, :d - delta squad."
Expand Down
Loading

0 comments on commit 4c853a2

Please sign in to comment.