Skip to content

Commit

Permalink
Round End Credits (#439)
Browse files Browse the repository at this point in the history
## Pull Request Hakkında
Ports Round End Credits from
Monkestation/Monkestation2.0#3301

Oyunun sonuna credits ekler.
Creditsin başlığı turda olan bir olaya göre değişir. Örnek olarak tur
sonunda kimse ölü değilse onunla alakalı bir başlık yazar.

https://github.com/user-attachments/assets/1788bf9c-e1df-4c93-9c8f-9bce0faad2d9

## Oyun İçin Neden Gerekli
Oyunun sonunda oyunda yaşanan şeylerin sanki bir filmmiş ve bitmiş gibi
olması, oyuncularda filmin oyuncuları gibi gösterilmesi bence hoş
duruyor. Bu yüzden oyuna eklemek istedim.
## Changelog
:cl: Rengan
add: Round Ende Credits eklendi, eğer görmek istesemezseniz Game
Preferences > UI > Show Roundend Credits tikini kapatabilirsiniz.
/:cl:
  • Loading branch information
Seefaaa authored Feb 22, 2025
2 parents 806b445 + 877fd7e commit 935db23
Show file tree
Hide file tree
Showing 25 changed files with 881 additions and 37 deletions.
1 change: 1 addition & 0 deletions code/__DEFINES/subsystems.dm
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@
#define INIT_ORDER_MINOR_MAPPING -40
#define INIT_ORDER_PATH -50
#define INIT_ORDER_EXPLOSIONS -69
#define INIT_ORDER_CREDITS -93
#define INIT_ORDER_STATPANELS -97
#define INIT_ORDER_BAN_CACHE -98
#define INIT_ORDER_INIT_PROFILER -99 //Near the end, logs the costs of initialize
Expand Down
91 changes: 91 additions & 0 deletions code/__HELPERS/icons.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1290,3 +1290,94 @@ GLOBAL_LIST_EMPTY(transformation_animation_objects)
if(PLANE_TO_TRUE(underlay.plane) != base_plane)
appearance.underlays -= underlay
return appearance


/// Renders a ckey's preferences appearance from their savefile
/proc/render_offline_appearance(ckey, mob/living/carbon/human/dummy/our_human)
if(!ckey || is_guest_key(ckey) || (!isnull(our_human) && !istype(our_human)))
return FALSE
var/save_path = "data/player_saves/[ckey[1]]/[ckey]/preferences.json"
if(!fexists(save_path))
return FALSE
var/list/tree = json_decode(rustg_file_read(save_path)) // Reading savefile
var/default_slot = tree["default_slot"] || 1
var/selected_char = tree["character[default_slot]"] || tree["character1"]

var/list/job_preferences = SANITIZE_LIST(selected_char?["job_preferences"])

var/datum/job/selected_job
var/highest_pref = 0

for(var/job in job_preferences)
if(job_preferences[job] > highest_pref)
selected_job = SSjob.get_job(job)
highest_pref = job_preferences[job]

if(selected_job && !ispath(selected_job::spawn_type, /mob/living/carbon/human)) // If the selected job's spawn_type isnt human (AI, cyborg etc.) we just returning mutable_appearance
var/mob/living/spawn_type = selected_job::spawn_type
var/mutable_appearance/appearance = mutable_appearance(spawn_type::icon, spawn_type::icon_state)
appearance.name = ckey
if(ispath(spawn_type, /mob/living/silicon/ai))
var/ai_core_value = selected_char?["preferred_ai_core_display"]
appearance.icon_state = resolve_ai_icon_sync(ai_core_value)
return appearance

var/we_created = FALSE
if(isnull(our_human))
our_human = new()
we_created = TRUE
else
our_human.wipe_state() // We're wiping the dummys overlays and outfit
our_human.set_species(/datum/species/human) // We're setting it to human beacuse if the savefile doesnt have species entry, it doesnt use previous icon's species
our_human.icon_render_keys = list()
our_human.update_body(is_creating = TRUE) // We're recreating bodyparts etc.

for (var/datum/preference/preference as anything in get_preferences_in_priority_order()) // Apply the preferences in priority order
if (preference.savefile_identifier != PREFERENCE_CHARACTER)
continue
var/saved_data = selected_char?[preference.savefile_key]
if(!saved_data)
continue
var/new_value = preference.deserialize(saved_data)

preference.apply_to_human(our_human, new_value)

var/datum/outfit/equipped_outfit

if(selected_job) // Selecting and creating outfit datum
var/datum/outfit/outfit_type
if(selected_job::outfit)
outfit_type = selected_job::outfit
if(selected_char?["species"] == SPECIES_PLASMAMAN && selected_job::plasmaman_outfit) // If they are plasmaman, give them plasmaman outfit
outfit_type = selected_job::outfit
if(outfit_type)
equipped_outfit = new outfit_type()
else
equipped_outfit = new()

var/list/loadout_preference_list = SANITIZE_LIST(selected_char?["loadout_list"])
var/datum/preference/loadout/loadout_preference = GLOB.preference_entries[/datum/preference/loadout]

var/list/loadout_datums = loadout_list_to_datums(loadout_preference.deserialize(loadout_preference_list))

for(var/datum/loadout_item/item as anything in loadout_datums) // Adding loadout items to outfit
item.insert_path_into_outfit(equipped_outfit, our_human, TRUE)

equipped_outfit.equip(our_human, TRUE)

var/list/all_quirks = SANITIZE_LIST(selected_char?["all_quirks"])
if(SSquirks?.initialized)
our_human.cleanse_quirk_datums() // We need to clean all the quirk datums every time
for(var/quirk_name as anything in all_quirks)
var/datum/quirk/quirk_type = SSquirks.quirks[quirk_name]
if(!(initial(quirk_type.quirk_flags) & QUIRK_CHANGES_APPEARANCE))
continue
our_human.add_quirk(quirk_type)

var/mutable_appearance/appearance = new
appearance.appearance = our_human.appearance
appearance.name = ckey
if(we_created)
qdel(our_human)

return appearance
22 changes: 17 additions & 5 deletions code/__HELPERS/roundend.dm
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#define POPCOUNT_SURVIVORS "survivors" //Not dead at roundend
#define POPCOUNT_ESCAPEES "escapees" //Not dead and on centcom/shuttles marked as escaped
#define POPCOUNT_ESCAPEES_HUMANONLY "human_escapees"
#define POPCOUNT_ESCAPEES_HUMANONLY_LIST "human_escapees_list"
#define POPCOUNT_SHUTTLE_ESCAPEES "shuttle_escapees" //Emergency shuttle only.
#define POPCOUNT_STATION_INTEGRITY "station_integrity"
#define PERSONAL_LAST_ROUND "personal last round"
#define SERVER_LAST_ROUND "server last round"

Expand All @@ -14,7 +17,9 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())
var/list/file_data = list("escapees" = list("humans" = list(), "silicons" = list(), "others" = list(), "npcs" = list()), "abandoned" = list("humans" = list(), "silicons" = list(), "others" = list(), "npcs" = list()), "ghosts" = list(), "additional data" = list())
var/num_survivors = 0 //Count of non-brain non-eye mobs with mind that are alive
var/num_escapees = 0 //Above and on centcom z
var/num_shuttle_escapees = 0 //Above and on escape shuttle
var/num_human_escapees = 0 //Above but humans only
var/num_shuttle_escapees = 0 //Above's above and on escape shuttle
var/list/list_of_human_escapees = list() //References to all escaped humans
var/list/area/shuttle_areas
if(SSshuttle?.emergency)
shuttle_areas = SSshuttle.emergency.shuttle_areas
Expand All @@ -39,6 +44,9 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())
escape_status = "escapees"
if(shuttle_areas[get_area(M)])
num_shuttle_escapees++
if(ishuman(M))
num_human_escapees++
list_of_human_escapees += M
if(isliving(M))
var/mob/living/L = M
mob_data["location"] = get_area(L)
Expand Down Expand Up @@ -91,8 +99,8 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())

var/datum/station_state/end_state = new /datum/station_state()
end_state.count()
var/station_integrity = min(PERCENT(GLOB.start_state.score(end_state)), 100)
file_data["additional data"]["station integrity"] = station_integrity
var/roundend_station_integrity = min(PERCENT(GLOB.start_state.score(end_state)), 100)
file_data["additional data"]["station integrity"] = roundend_station_integrity
WRITE_FILE(json_file, json_encode(file_data))

SSblackbox.record_feedback("nested tally", "round_end_stats", num_survivors, list("survivors", "total"))
Expand All @@ -102,8 +110,10 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())
. = list()
.[POPCOUNT_SURVIVORS] = num_survivors
.[POPCOUNT_ESCAPEES] = num_escapees
.[POPCOUNT_ESCAPEES_HUMANONLY] = num_human_escapees
.[POPCOUNT_SHUTTLE_ESCAPEES] = num_shuttle_escapees
.["station_integrity"] = station_integrity
.[POPCOUNT_ESCAPEES_HUMANONLY_LIST] = list_of_human_escapees
.[POPCOUNT_STATION_INTEGRITY] = roundend_station_integrity

/datum/controller/subsystem/ticker/proc/gather_antag_data()
var/team_gid = 1
Expand Down Expand Up @@ -220,6 +230,9 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())
LAZYCLEARLIST(round_end_events)

var/speed_round = (STATION_TIME_PASSED() <= 10 MINUTES)
popcount = gather_roundend_feedback()
SScredits.draft()
SScredits.finalize()

for(var/client/C in GLOB.clients)
if(!C?.credits)
Expand All @@ -229,7 +242,6 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())
C?.give_award(/datum/award/achievement/misc/speed_round, C?.mob)
HandleRandomHardcoreScore(C)

var/popcount = gather_roundend_feedback()
display_report(popcount)

CHECK_TICK
Expand Down
100 changes: 73 additions & 27 deletions code/_onclick/hud/credits.dm
Original file line number Diff line number Diff line change
@@ -1,28 +1,43 @@
#define CREDIT_ROLL_SPEED 125
#define CREDIT_SPAWN_SPEED 10
#define CREDIT_ANIMATE_HEIGHT (14 * ICON_SIZE_Y)
#define CREDIT_EASE_DURATION 22
#define CREDITS_PATH "[global.config.directory]/contributors.dmi"
#define CREDIT_ROLL_SPEED 9 SECONDS
#define CREDIT_SPAWN_SPEED 1 SECONDS
#define CREDIT_ANIMATE_HEIGHT (16 * world.icon_size)
#define CREDIT_EASE_DURATION 2.2 SECONDS

/client/proc/RollCredits()
set waitfor = FALSE
if(!fexists(CREDITS_PATH))
if(!prefs?.read_preference(/datum/preference/toggle/show_roundend_credits))
return
var/icon/credits_icon = new(CREDITS_PATH)
LAZYINITLIST(credits)
var/list/_credits = credits
add_verb(src, /client/proc/ClearCredits)
var/static/list/credit_order_for_this_round
if(isnull(credit_order_for_this_round))
credit_order_for_this_round = list("Thanks for playing!") + (shuffle(icon_states(credits_icon)) - "Thanks for playing!")
var/list/credit_order_for_this_round = SScredits.credit_order_for_this_round

var/count = 0
for(var/I in credit_order_for_this_round)
if(!credits)
return
_credits += new /atom/movable/screen/credit(null, null, I, src, credits_icon)
sleep(CREDIT_SPAWN_SPEED)
if(istype(I, /obj/effect/title_card_object)) //huge image sleep
sleep(CREDIT_SPAWN_SPEED * 3.3)
count = 0
if(count && !istype(I, /mutable_appearance) && !istype(I, /obj/effect/cast_object))
sleep(CREDIT_SPAWN_SPEED)

_credits += new /atom/movable/screen/credit(null, null, I, src)
if(istype(I, /mutable_appearance))
count++
if(count >= 6)
count = 0
sleep(CREDIT_SPAWN_SPEED)
else if(istype(I, /obj/effect/cast_object))
count++
if(count >= 2)
count = 0
sleep(CREDIT_SPAWN_SPEED)
else
sleep(CREDIT_SPAWN_SPEED)
count = 0
sleep(CREDIT_ROLL_SPEED - CREDIT_SPAWN_SPEED)
remove_verb(src, /client/proc/ClearCredits)
qdel(credits_icon)
INVOKE_ASYNC(src, TYPE_PROC_REF(/client, ClearCredits))

/client/proc/ClearCredits()
set name = "Hide Credits"
Expand All @@ -34,35 +49,67 @@
/atom/movable/screen/credit
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
alpha = 0
screen_loc = "12,1"
plane = SPLASHSCREEN_PLANE
screen_loc = "3,1"
var/client/parent
var/matrix/target

/atom/movable/screen/credit/Initialize(mapload, datum/hud/hud_owner, credited, client/P, icon/I)
/atom/movable/screen/credit/Initialize(mapload, datum/hud/hud_owner, credited, client/P)
. = ..()
icon = I
parent = P
icon_state = credited
maptext = MAPTEXT_PIXELLARI(credited)
maptext_x = ICON_SIZE_X + 8
maptext_y = (ICON_SIZE_Y / 2) - 4
maptext_width = ICON_SIZE_X * 3
var/view = P?.view
var/list/offsets = screen_loc_to_offset("3,1", view)

if(istype(credited, /mutable_appearance))
var/mutable_appearance/choice = credited
choice.plane = plane
choice.screen_loc = screen_loc
choice.alpha = alpha
maptext_width = choice.maptext_width
maptext = choice.maptext
appearance = choice.appearance
screen_loc = offset_to_screen_loc(offsets[1] + choice.pixel_x, offsets[2] + choice.pixel_y)
add_overlay(choice)

if(istype(credited, /obj/effect/title_card_object))
var/obj/effect/title_card_object/choice = credited
choice.plane = plane
choice.screen_loc = screen_loc
choice.alpha = alpha
maptext_width = choice.maptext_width
maptext = choice.maptext
appearance = choice.appearance
screen_loc = offset_to_screen_loc(offsets[1] + choice.pixel_x, offsets[2] + choice.pixel_y)
add_overlay(choice)

if(istype(credited, /obj/effect/cast_object))
var/obj/effect/cast_object/choice = credited
maptext = MAPTEXT_PIXELLARI(choice.maptext)
maptext_x = choice.maptext_x
maptext_y = choice.maptext_y
maptext_width = choice.maptext_width
maptext_height = choice.maptext_height

if(istext(credited))
maptext = MAPTEXT_PIXELLARI(credited)
maptext_x = world.icon_size + 8
maptext_y = (world.icon_size / 2) - 4
maptext_width = world.icon_size * 12
maptext_height = world.icon_size * 3

var/matrix/M = matrix(transform)
M.Translate(0, CREDIT_ANIMATE_HEIGHT)
animate(src, transform = M, time = CREDIT_ROLL_SPEED)
target = M
animate(src, alpha = 255, time = CREDIT_EASE_DURATION, flags = ANIMATION_PARALLEL)
addtimer(CALLBACK(src, PROC_REF(FadeOut)), CREDIT_ROLL_SPEED - CREDIT_EASE_DURATION)
QDEL_IN(src, CREDIT_ROLL_SPEED)
if(parent)
parent.screen += src
parent?.screen += src

/atom/movable/screen/credit/Destroy()
icon = null
if(parent)
parent.screen -= src
LAZYREMOVE(parent.credits, src)
parent.credits -= src
parent = null
return ..()

Expand All @@ -73,4 +120,3 @@
#undef CREDIT_EASE_DURATION
#undef CREDIT_ROLL_SPEED
#undef CREDIT_SPAWN_SPEED
#undef CREDITS_PATH
Loading

0 comments on commit 935db23

Please sign in to comment.