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

Overhauls the job menu thing #5335

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
118 changes: 118 additions & 0 deletions CMLS.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# HOW TO CMLS

There are only two things to worry about: The Gun and the Ammo Kind

### AmmoKind
This defines all the settings for the casing, projectile, magazine, and ammobox. This datum, depending on the vars you set, will automatically:
- Generate a box and/or a crate that'll be added to the CMLS vendor
- Generate a projectile, casing, and magazine
- Load all the sprites from the associated icon file

Lets say you want to add in 4.92x14mm Scrungy. This'll be a basic Medium AmmoKind. It would look something like this:
```C
/datum/ammo_kind/medium/q_4_92x14mm_scrungy
name = "4.92x14mm Scrungy"
bullet_flavor = "A very scrungy bullet." // You can paste in somme bullshit AI algovomit here, the longer the better, it will show up on the casing (but not the magazine!)."
casing_kind = "cartridge" // 'you load a 4.92x14mm Scrungy cartridge into the gun'
projectile_kind = "bullet" // 'you are hit by a 4.92x14mm Scrungy bullet!'
box_name = "box of 4.92x14mm Scrungy bullets" // name of the associated box of bullets
box_flavor = "Algovomit goes here" // desc of the box
crate_name = "crate of 4.92x14mm Scrungy bullets" // name of the associated crate
crate_flavor = "More algovomit" // desc of the crate
magazine_name = "compact magazine" // if your gun can eject a magazine, it makes a magazine with this name when you eject it
magazine_flavor = "Its a magazine!" // desc of that magazine
caliber = CALIBER_COMPACT // the caliber of the casing, boxes, crates, and magazines
sound_properties = CSP_PISTOL_LIGHT // the sounds this bullet makes (look up [code\modules\projectiles\ammo_casing_gun_sound_properties.dm])
ammo_icon = 'icons/obj/ammo/compact.dmi' // the icon that the datum pulls all its sprites from (it does this automatically!)
damage_list = list(
"30" = 30,
"35" = 10,
"40" = 1,
"200" = 1,
) // the list of damages this bullet will do. All statistical things are calculated by the datum from this list. If you have a really high value in there somewhere, the datum will interpret that as a crit, and generate a statblock accordingly.
damage_type = BRUTE // Damage type of the projectile, look up [code\__DEFINES\combat.dm] around line 6ish
damage_armor = "bullet" // the armor type the projectile checks against
pellet_count = 1 // number of pellets, used for shotguns
caseless = FALSE // Deletes the casing on shooting, not sure if it works
```

For any children of this AmmoKind, all you really need are the names and flavors. If any of the names or flavors are not set, the AmmoKind will automatically generate somewhat fitting names and flavors for whatever's missing. For instance, this is a perfectly valid AmmoKind:

```C
/datum/ammo_kind/medium/q_4_92x14mm_scrungy
name = "4.92x14mm Scrungy"
```

It will inherit all the vars from ammo_kind/medium!

If your AmmoKind doesn't have any special sprites (as in, the projectile, casing, box, crate, and magazine don't need to look any different from the parent AmmoKind), you're done for the AmmoKind section! Yay! We'll get into how to make it look different later.

### Gun
Ballistic guns can be CMLSed!
Say you want to make a gun for that Scrungy round. A basic one would look like this:

```C
/obj/item/gun/ballistic/scrungy_classic
name = "Superduper Scrungy Classic"
desc = "This gun sucks (and swallows)"

use_cmls = TRUE // Forces the gun to use the CMLS system
var/damage_list = list(
"10" = 50,
"1" = 2,
"40" = 2,
) // If set, these values will be used instead of the damages in AmmoKind
damage_type = BRUTE // Overrides the damage type of the projectile. Can be null to use the AmmoKind's value
damage_armor_type = "bullet" // Overrides the armor check of the projectile. Can be null to use the AmmoKind's value
ammo_kind = /datum/ammo_kind/medium/q_4_92x14mm_scrungy // the AmmoKind that this gun will use. It will set up everything on the gun, nice and easy
ammo_magazine_name = "%MAXAMMO% round clipazine" // Name of the magazine inside the gun, for the text used when you 'eject' the magazine
ammo_capacity = 10 // How many bullets can go in the gun
ammo_single_load = FALSE // Whether or not you can only load one bullet at a time
is_revolver = FALSE // when you go to eject the magazine, it instead just dumps out the casings, like a revolver
sound_magazine_eject = "gun_remove_empty_magazine" // sound it makes when you eject the magazine, if applicable
var/sound_magazine_insert = "gun_insert_full_magazine" // sound it makes when you insert a magazine, if applicable
```

And that's it! Your gun (should) be fully functional at this point! Do note that the AmmoKind sprite cataloguer does *not* handle the gun's sprites, those are still handled in the same way as before, so be sure your gun isn't invisible after your changes!

### Sprites
AmmoKinds automatically read the icon states in their ammo_icon and catalogue all the sprites associated with the kind of ammo it is, handling all that mess on its end! The way it does this is that it runs through the names of each icon_state, reads certain keywords, and categorizes them accordingly.

These names are made up of one of two sets of tokens:

"CORB-suffix" for states with no variation (full boxes, empty boxes, etc)

"CORB-suffix-partial-key" for states that vary based on the number of bullets in the associated box

CORB can be one of four things:
- bullet
- box
- crate
- magazine

Suffix can be one of four things:
- projectile
- full
- empty
- partial

Partial can be one of three things:
- broad
- percent
- count

Key depends on if Partial is percent or count
- For percent, it will display this sprite if the ammobox is less than this percent full of bullets
- For count, it will display this sprite if the ammobox contains this amount or less of bullets inside

I'll expand on this later, but the attached images should explain at least some of it!










34 changes: 34 additions & 0 deletions code/__DEFINES/jobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@
#define JOB_UNAVAILABLE_SLOTFULL 5
#define JOB_UNAVAILABLE_SPECIESLOCK 6
#define JOB_UNAVAILABLE_WHITELIST 7
#define JOB_UNAVAILABLE_NOT_NEWPLAYER 8

#define DEFAULT_RELIGION "Christianity"
#define DEFAULT_DEITY "Space Jesus"
Expand Down Expand Up @@ -414,3 +415,36 @@
#define JOB_DISPLAY_ORDER_KHORCHIN 131
#define JOB_DISPLAY_ORDER_KIPCHAK 132
#define JOB_DISPLAY_ORDER_MANGUDAI 133

#define REC_SKILL_COMBAT_LVL1 "Combat - Lvl I" = \
"You can't go two steps outside without running into something that wants to test the limits of your (and its) immortality. \
Being able to defend yourself is a must, whether it's with a gun, a sword, or your bare hands. \
Most critters you'll encounter in the wastes are fairly low-threat, such as Pillbugs, Rats, and Geckos, which will be \
good practice for the more dangerous creatures you'll encounter later on. \
Your survival knife is surprisingly effective! If you are on Harm intent (the red one), if you click past a critter, you'll \
swing at it with your knife. Guns are a simple point-and-click affair, but most require either bullets, magazines full of bullets, \
batteries, or just a good pump to work. You can quickly reload guns by pressing (by default) ctrl-R. \
Some guns need to be racked or cocked after each shot, and this is done by pressing the use key (default: C) while holding the gun."
#define REC_SKILL_HEALING_LVL1 "Healing - Lvl I" = \
"You're going to get hurt. A lot. So, you'll want to be able to patch yourself up when you do, preferably before you \
die. Thankfully, first aid is fairly simple! There are yellow Broc flowers scattered throughout the wastes that, when eaten, \
will heal your bruises, albeit slowly. You can also find Stimpaks, which are a bit more potent, but harder to come by. If you \
craft a Broc flower with a Xander root (the brown ones), you'll get some healing powder that'll heal both bruises and burns, \
and fairly quickly at that. If all else fails, click the green cross on the bottom right of your screen, then use whatever \
it spawns on yourself to heal yourself up -- be sure to drink water if you're doing this!"
#define REC_SKILL_TRAUMA_LVL1 "Trauma - Lvl I" = \
"Most folk bleed when they are injured, and you are (probably) no exception. If you find yourself leaking red stuff, \
find a bandage and click yourself with it to apply it to any wounds you've taken. If you have sutures, you can use them \
too, and if you have both bandages and sutures applied, you'll heal up even faster! Do note that if you are injured while \
you have a bandage or suture applied, they'll be destroyed and you'll be right back to bleeding! As your wounds heal up, \
be sure to stay good and fed, as this speeds along the process."
#define REC_SKILL_SURVIVAL_LVL1 "Survival - Lvl I" = \
"Surviving in the wastes is a tough business, but you're a tough person! You'll need to eat and drink to keep yourself \
alive, and you can do this by finding food and water in the wastes. You can find food in the form of various plants, \
animals, and pre-event foodstuffs, and you can find water in the form of bottles of water, ponds, and sinks. You can \
cook most meats by sticking them into a microwave or oven (found in most houses)."





3 changes: 2 additions & 1 deletion code/_onclick/hud/_defines.dm
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@

#define ui_character_actions "EAST-1:28, SOUTH+1:2"
#define ui_bayou "EAST-1:28, SOUTH+0:2" //Character directory
#define ui_pvpbuttons "EAST-1:28, SOUTH+1:18" //slut directory
#define ui_pvpbuttons "EAST-1:28, SOUTH+1:18" //sludt directory
#define ui_job_viewer "EAST-1:28, SOUTH+2:0" //job viewer

//living
#define ui_living_pull "EAST-1:28,CENTER-2:15"
Expand Down
1 change: 1 addition & 0 deletions code/_onclick/hud/hud.dm
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
var/atom/movable/screen/newbie_hud_button
var/atom/movable/screen/chardir_hud_button
var/atom/movable/screen/pvp_focus_toggle/pvp_focus_toggle
var/atom/movable/screen/job_button

// subtypes can override this to force a specific UI style
var/ui_style
Expand Down
4 changes: 4 additions & 0 deletions code/_onclick/hud/human.dm
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,10 @@
pvp_focus_toggle.hud = src
infodisplay += pvp_focus_toggle

job_button = new /atom/movable/screen/job_button()
job_button.hud = src
infodisplay += job_button

pull_icon = new /atom/movable/screen/pull()
pull_icon.icon = ui_style
pull_icon.hud = src
Expand Down
10 changes: 10 additions & 0 deletions code/_onclick/hud/screen_objects/character_actions.dm
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@
if(usr.client)
usr.client.show_character_directory()

/atom/movable/screen/job_button
name = "Review Role"
desc = "Review your current role, as well as the roles of others."
icon = 'icons/mob/screen_gen.dmi'
icon_state = "jobviewer"
screen_loc = ui_job_viewer

/atom/movable/screen/job_button/Click(location,control,params)
SSjob.ShowJobPreview(usr)

/atom/movable/screen/pvp_focus_toggle
name = "PVP focus On/Off"
icon = 'icons/mob/screen_gen.dmi'
Expand Down
135 changes: 135 additions & 0 deletions code/controllers/subsystem/job.dm
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ SUBSYSTEM_DEF(job)

var/list/level_order = list(JP_HIGH,JP_MEDIUM,JP_LOW)

var/list/preview_holders = list()

/datum/controller/subsystem/job/Initialize(timeofday)
SSmapping.HACK_LoadMapConfig()
if(!occupations.len)
Expand Down Expand Up @@ -819,3 +821,136 @@ SUBSYSTEM_DEF(job)

/datum/controller/subsystem/job/proc/JobDebug(message)
log_job_debug(message)

/datum/controller/subsystem/job/proc/ShowJobPreview(ckey)
ckey = extract_ckey(ckey)
if(!ckey)
return
var/datum/job_preview_holder/jph = preview_holders[ckey]
if(!jph)
jph = new /datum/job_preview_holder(ckey)
preview_holders[ckey] = jph
return jph.showme()

/// A holder for the TGUI job preview thing!!
/datum/job_preview_holder
var/datum/job/my_job
var/my_ckey
var/list/tgui_slugs = list()

/datum/job_preview_holder/New(ckey)
. = ..()
my_ckey = ckey

/datum/job_preview_holder/proc/compile_tgui_map()
tgui_slugs.Cut()
var/list/categories = GLOB.position_ordering.Copy()
for(var/cat in GLOB.position_categories)
if(!islist(categories[cat]))
categories[cat] = list()
var/list/categlob = GLOB.position_categories[cat]
var/list/category_slug = list()
category_slug["CatColor"] = categlob["color"]
category_slug["CatTitle"] = cat
category_slug["CatJobs"] = list() // merek gives good catjobs
/// now fill them with some jobs
for(var/cjob in categlob["jobs"])
var/datum/job/job = SSjob.GetJob(cjob)
if(!job)
continue
var/list/tug_slug = job.get_tgui_slug()
if(!tug_slug)
continue
tug_slug["Category"] = cat
category_slug["CatJobs"] += list(tug_slug)
categories[cat] = category_slug
// trim empty categories
for(var/cat in categories)
if(!LAZYLEN(categories[cat]))
categories -= cat
// then add the naked slugs to the list, to preserve order
for(var/cat in categories)
tgui_slugs += list(categories[cat])

/datum/job_preview_holder/proc/show_main()
var/mob/user = ckey2mob(my_ckey)
ui_interact(user)
return TRUE

/datum/job_preview_holder/proc/showme()
var/mob/user = ckey2mob(my_ckey)
ui_interact(user)
return TRUE

/datum/job_preview_holder/ui_state(mob/user)
return GLOB.always_state

/datum/job_preview_holder/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "JobPreview")
ui.open()
ui.set_autoupdate(FALSE)
update_static_data(user, ui)

/datum/job_preview_holder/ui_static_data(mob/user)
. = ..()
if(!user || !user.client)
return
compile_tgui_map()
var/list/slugpower = tgui_slugs.Copy()
/// cleverly slot the player-specific data into the jobs within the slugs
var/list/xps = user?.client?.prefs?.exp.Copy()
for(var/list/cat in slugpower)
for(var/list/job in cat["CatJobs"])
var/datum/job/j = SSjob.GetJob(job["Title"])
if(!j)
continue
var/my_time = LAZYACCESS(xps, j.exp_type)
if(my_time < 1)
job["CurrentMinutes"] = "0m"
else
job["CurrentMinutes"] = DisplayTimeText(my_time * 10 * 60, abbreviated = TRUE) || "0m"
var/timeleft = 0
if(istype(j))
timeleft = j.required_playtime_remaining(user.client)
job["RawTimeLeft"] = timeleft
job["TimeTillCanSpawn"] = "[DisplayTimeText(timeleft * 10 * 60, abbreviated = TRUE)]"
if(isnewplayer(user))
var/mob/dead/new_player/player = user
job["SpawnFailure"] = player.IsJobUnavailable(j.title, latejoin = TRUE)
else
job["SpawnFailure"] = JOB_UNAVAILABLE_NOT_NEWPLAYER
var/list/data = list()
data["AllJobsNCats"] = slugpower
return data

/datum/job_preview_holder/ui_data(mob/user)
. = ..()
var/list/data = list()
data["IsInGame"] = !isnewplayer(user)
data["MyCkey"] = user.client?.ckey || my_ckey
data["CurrentJobTitle"] = user.mind?.assigned_role || "Assistant"
return data

/datum/job_preview_holder/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/mob/user = ckey2mob(params["MyCkey"])
if(!user)
return
if(action != "JoinJob")
return
if(!istype(user, /mob/dead/new_player))
user.playsound_local(user, 'sound/machines/low_buzz.ogg', 80, TRUE)
to_chat(user, span_alert("You'll want to respawn and mess with this from the lobby screen if you want to become this job."))
return
var/mob/dead/new_player/player = user
if(!player.AttemptLateSpawn(params["JoinJob"]))
return
ui = SStgui.try_update_ui(user, src, ui)
if(ui)
ui.close()




Loading