diff --git a/code/__DEFINES/stats_and_skills.dm b/code/__DEFINES/stats_and_skills.dm new file mode 100644 index 0000000000..3c336c0034 --- /dev/null +++ b/code/__DEFINES/stats_and_skills.dm @@ -0,0 +1,9 @@ +#define STAT_STR "Brawn" +#define STAT_PER "Awareness" +#define STAT_END "Toughness" +#define STAT_CHA "Moxie" +#define STAT_INT "Smarts" +#define STAT_AGI "Deftness" +#define STAT_LCK "Fate" +#define STAT_GEN "Flat Roll" +#define STAT_SUF "Crit" diff --git a/code/_onclick/hud/screen_objects/character_actions.dm b/code/_onclick/hud/screen_objects/character_actions.dm index e57721e5e8..d2cead1b12 100644 --- a/code/_onclick/hud/screen_objects/character_actions.dm +++ b/code/_onclick/hud/screen_objects/character_actions.dm @@ -291,13 +291,13 @@ /atom/movable/screen/roll_hud_button/Click(location,control,params,) // This stuff needs to be changed because it was directly lifted from clothing var/static/list/choices = list( - "Brawn" = image(icon = 'icons/obj/stationary.dmi', icon_state = "fitnessweight-w"), - "Awareness" = image(icon = 'icons/obj/status_display.dmi', icon_state = "ai_friend"), - "Toughness" = image(icon = 'modular_coyote/icons/objects/weapons.dmi', icon_state = "imperial_kite"), - "Moxie" = image(icon = 'icons/mob/screen_gen.dmi', icon_state = "mood9"), - "Smarts" = image(icon = 'modular_roguetown/items/books.dmi', icon_state = "ledger0"), - "Deftness" = image(icon = 'icons/obj/implants.dmi', icon_state = "warp"), - "Fate" = image(icon = 'icons/obj/economy.dmi', icon_state = "coin_iron_flip"), + "Brawn" = image(icon = 'icons/mob/screen_gen.dmi', icon_state = "brawn"), + "Awareness" = image(icon = 'icons/mob/screen_gen.dmi', icon_state = "awareness"), + "Toughness" = image(icon = 'icons/mob/screen_gen.dmi', icon_state = "toughness"), + "Moxie" = image(icon = 'icons/mob/screen_gen.dmi', icon_state = "moxie"), + "Smarts" = image(icon = 'icons/mob/screen_gen.dmi', icon_state = "smarts"), + "Deftness" = image(icon = 'icons/mob/screen_gen.dmi', icon_state = "deftness"), + "Fate" = image(icon = 'icons/mob/screen_gen.dmi', icon_state = "fate"), ) var/mob/user = usr var/choice = show_radial_menu(user, src, choices, radius = 32,) diff --git a/code/controllers/subsystem/stats_attributes_skills.dm b/code/controllers/subsystem/stats_attributes_skills.dm new file mode 100644 index 0000000000..dda0b13e87 --- /dev/null +++ b/code/controllers/subsystem/stats_attributes_skills.dm @@ -0,0 +1,567 @@ +/* + * File: stats_n_skills.dm + * Author: Bingfox + * + */ + +SUBSYSTEM_DEF(sas) + name = "SAS" + priority = FIRE_PRIORITY_MOBS + flags = SS_KEEP_TIMING | SS_NO_INIT + runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + + // master list of defaults + var/list/reference_stocks = list() + var/list/reference_stats = list() + var/list/reference_skills = list() + var/list/stat_invokers = list() + var/max_total_stats = 40 + // everyone's thingy! + var/list/sheets = list() + +/datum/controller/subsystem/sas/Initialize(start_timeofday) + build_statskills() + build_stocks() + . = ..() + +/datum/controller/subsystem/sas/proc/build_statskills() + for(var/skul in subtypesof(/datum/statskill)) + var/datum/statskill/S = new skul() + if(S.is_skill) + reference_skills[S.key] = S + if(S.is_stat) + reference_stats[S.key] = S + +/datum/controller/subsystem/sas/proc/build_stocks() + for(var/skul in subtypesof(/datum/stats_and_skills_holder)) + var/datum/stats_and_skills_holder/S = new skul() + reference_stocks[S.key] = S + +/datum/controller/subsystem/sas/proc/give_holder_to_player(someone) + var/p_quid = SSeconomy.extract_quid(someone) + if(LAZYACCESS(sheets, p_quid)) + return // already have their thing there! + var/mob/player = extract_mob(someone) + if(!player || player.sas_key == SAS_DEFAULT) + return + var/datum/stats_and_skills/SAS = new() + sheets["[p_quid]"] = SAS + SAS.set_player(p_quid) + SAS.load_stats() + player.sas_key = "[p_quid]" + +/datum/controller/subsystem/sas/proc/get_sas(someone) + var/mob/such = extract_mob(such) + if(!such) + return + var/datum/stats_and_skills/ses = LAZYACCESS(sheets, such.sas_key) + if(!ses) // invalid ses, give em a default (might be a player, that'll be fixed soon enuf) + such.sas_key = "default" + ses = LAZYACCESS(sheets, such.sas_key) + return ses + +/datum/controller/subsystem/sas/proc/get_stat(someone, which) + var/datum/stats_and_skills/ses = get_sas(someone) + return LAZYACCESS(ses.stats, which) + +/datum/controller/subsystem/sas/proc/do_roll(performer, stat, threshold, flags) + var/datum/stats_and_skills/doer_sass = get_sas(performer) + var/datum/roll_results/doer_roll = doer_sass.do_roll(stat, threshold, flags) + return doer_roll + +/datum/controller/subsystem/sas/proc/get_skills() + var/list/skullz = list() + for(var/skill in reference_skills) + var/datum/statskill/S = reference_skills[skill] + skullz += S.displayname + +/datum/controller/subsystem/sas/proc/get_stat_triggers() + var/list/skullz = list() + for(var/ivk in stat_invokers) + if(!islist(skullz[stat_invokers[ivk]])) + skullz[stat_invokers[ivk]] = list() + skullz[stat_invokers[ivk]] |= lowertext(ivk) + var/list/retmsg = list() + for(var/kind in skullz) + retmsg += "[kind]" + for(var/bingus in skullz[kind]) + retmsg += "[bingus]" + return retmsg.Join("\n") + +/datum/controller/subsystem/sas/proc/get_num_dice(dicetring) + var/list/splode = splode_dice(dicetring) + if(splode["bad"]) + return 1 + return numberfy(LAZYACCESS(splode, "DICE")) + +/datum/controller/subsystem/sas/proc/get_crit_success(dicetring) + var/list/splode = splode_dice(dicetring) + if(splode["bad"]) + return 20 + return numberfy(LAZYACCESS(splode, "SIDES")) + +/datum/controller/subsystem/sas/proc/get_crit_fail(dicetring) + return 1 + +/// Expects format: "NUMdNUM+NUM" +/datum/controller/subsystem/sas/proc/splode_dice(dicetring) + dicetring = lowertext(dicetring) + var/list/split1 = splittext(dicetring, "d") + var/numdice = LAZYACCESS(split1, 1) + if(numdice > 2) + return list("bad" = TRUE) + replacetext(LAZYACCESS(split1, 2), "+", "BBB") + replacetext(LAZYACCESS(split1, 2), "-", "BBB") + var/list/split2 = splittext(LAZYACCESS(split1, 2), "BBB") + if(LAZYLEN(split2) > 2) + return list("bad" = TRUE) + var/numsides = LAZYACCESS(split2, 1) + var/mod = LAZYACCESS(split2, 2) + . = list() + .["DICE"] = numdice || 0 + .["SIDES"] = numsides || 0, + .["MOD"] = mod || 0 + +/datum/stats_and_skills + var/stock_key + var/owner_quid + var/virgin = TRUE + var/list/stats = list( + STAT_STR = 5, + STAT_PER = 5, + STAT_END = 5, + STAT_CHA = 5, + STAT_INT = 5, + STAT_AGI = 5, + STAT_LCK = 5, + ) + var/list/skills = list() + var/max_total = 40 + +/datum/stats_and_skills/New() + . = ..() + var/list/nustats = list() + // first go through the stats and get our defaults + for(var/stat in SSsas.reference_stats) + var/datum/statskill/S = SSsas.reference_stats[stat] + var/datum/statskill/ours = new S.type() + if(stats[stat]) + ours.set_stat(stats[stat]) + nustats[ours.key] = ours + stats = nustats() + // now the skills + var/list/nuskills = list() + // first go through the stats and get our defaults + for(var/skill in SSsas.reference_skills) + var/datum/statskill/S = SSsas.reference_skills[skill] + var/datum/statskill/ours = new S.type() + if(skills[skill]) + ours.set_skill(skills[skill]) + nuskills[ours.key] = ours + skills = nuskills() + +/datum/stats_and_skills/proc/load_from_prefs() + if(!virgin) + return + virgin = FALSE + var/mob/plyr = extract_mob(smth) + if(!plyr) + return + var/datum/preferences/P = extract_prefs(plyr) + if(!P) + return + for(var/stat in stats) + var/datum/statskill/S = stats[stat] + S.load_from_prefs(P) + set_player(plyr) + if(hax()) // only applies to stats loaded from prefs + return + +/datum/stats_and_skills/proc/hax() + var/total = 0 + for(var/stat in stats) + var/datum/statskill/S = stats[stat] + total += S.current + if(total > SSsas.max_total_stats) + var/mob/master = get_owner() + if(master) + message_admins(span_alertalien("WEEOO WEEOO [master.name] ([master.ckey]) just spawned in with more than [max_total] stat points!!")) + master.gib() + return TRUE + +/datum/stats_and_skills/proc/set_player(mob/player) + owner_quid = SSeconomy.extract_quid(player) + +/datum/stats_and_skills/proc/do_roll(which, dice, threshold, flags = SAS_STAT | SAS_MODS) + if(!stats[which]) + return new /datum/roll_results() + if(!dice) + dice = "1d20" + var/datum/statskill/S = stats[which] + return S.rollstat(dice, threshold, flags) + +/datum/statskill // issue + var/key + var/is_skill = FALSE + var/is_stat = FALSE + var/displayname = "Stat" + var/default = 5 + var/maxval = 10 // only obeyed in the prefs menu! + var/minval = 1 + var/current = 1 + var/list/modifiers = list() + // hide from the prefs menu + var/hidden + /// rollmote stuff + var/list/rollmote_triggers = list() + var/list/rollmote_initial = list() + var/list/rollmote_success = list() + var/list/rollmote_success_crit = list( + "Ring-a-ding baby!", + "Wow!", + ) + var/list/rollmote_fail = list() + var/list/rollmote_fail_crit = list( + "OOF, what a spectacular failure!", + "The game was rigged from the start.", + ) + +/datum/statskill/New() + . = ..() + if(!SSsas.reference_stats[key]) + for(var/inv in rollmote_triggers) + SSsas.stat_invokers[lowertext(inv)] = key + SSsas.stat_invokers[key] = key // hue + SSsas.stat_invokers[lowertext(key)] = key // hue + SSsas.stat_invokers[displayname] = key // hue + SSsas.stat_invokers[lowertext(displayname)] = key // hue + +/datum/statskill/proc/set_stat(neval) + current = neval + +/datum/statskill/proc/rollstat(dice, threshold, flags) + dice = formattify_dice(dice) + var/datum/roll_return/RT = new() + var/mod = get_mofidiers() + // var/numdice = SSsas.get_num_dice(dice) + var/critsucc = SSsas.get_crit_success(dice) + var/critfail = SSsas.get_crit_fail(dice) + var/porm = mod >= 0 ? "+" : "-" + var/randroll = roll("[dice][porm][mod]") + RT.resultnum = randroll + RT.base = current + RT.mods = mod + RT.displayname = displayname + RT.pre_msg = formattify(safepick(rollmote_initial)) + if(!threshold || randroll >= threshold) + RT.passed = TRUE + RT.msg = formattify(safepick(rollmote_success)) + else + RT.passed = FALSE + RT.msg = formattify(safepick(rollmote_fail)) + if(randroll >= critsucc) + RT.crit_success = TRUE + RT.msg += " [formattify(safepick(rollmote_success_crit))]" + if(randroll <= critfail) + RT.crit_fail = TRUE + RT.msg += " [formattify(safepick(rollmote_success_fail))]" + return RT + +/datum/statskill/proc/formattify_dice(dice) + . = replacetext(dice, "$STATMODS", "[current + get_mods()]") + . = replacetext(dice, "$STAT", "[current]") + +/datum/statskill/proc/get_mods() + . = 0 + for(var/mod in modifiers) + . += modifiers[mod] + +/datum/statskill/strength + key = STAT_STR + displayname = "Strength" + is_stat = TRUE + rollmote_triggers = list( + "b" + "brn" + "brawn" + ) + rollmote_initial = list( + "%MOBNAME tests %MOBTHEIR brawn...", + "%MOBNAME flexes %MOBTHEIR arm(s)...", + "%MOBNAME prepares to lift...", + "%MOBNAME puts %MOBTHEIR back into it..." + ) + rollmote_success = list( + "%MOBNAME is strong!", + "%MOBNAME is beefy!", + "%MOBNAME has some serious guns!", + "%MOBNAME had some strength behind it!" + ) + rollmote_fail = list( + "%MOBNAME was too weak.", + "%MOBNAME was a little wet noodle.", + "%MOBNAME would loose an arm wrestling match with a mouse.", + "%MOBNAME has some serious atrophy. it's a wonder %MOBTHEY can move at all." + ) + +/datum/statskill/perception + key = STAT_PER + displayname = "Perception" + is_stat = TRUE + rollmote_triggers = list( + "a", + "aware", + "awareness", + ) + rollmote_initial = list( + "%MOBNAME takes a good, long look...", + "%MOBNAME squints...", + "%MOBNAME looks around...", + "%MOBNAME focuses in..." + ) + rollmote_success = list( + "%MOBNAME was perceptive!", + "%MOBNAME noticed things!", + "%MOBNAME has eyes like a hawk!", + "%MOBNAME could find Doc Mitchell's keys!", + "%MOBNAME noticed whatever %MOBTHEY were trying to see!" + ) + rollmote_fail = list( + "%MOBNAME was totally oblivious.", + "%MOBNAME forgot %MOBTHEIR glasses.", + "%MOBNAME didn't see anything." + ) + +/datum/statskill/endurance + key = STAT_END + displayname = "Endurance" + is_stat = TRUE + rollmote_triggers = list( + "tough", + "tuff", + "end", + "toughness", + ) + rollmote_initial = list( + "%MOBNAME tests %MOBTHEIR toughness...", + "%MOBNAME braces %MOBTHEMSELF..." + ) + rollmote_success = list( + "%MOBNAME was tough!", + "%MOBNAME was one tough cookie!", + "%MOBNAME doesn't even flinch!", + "%MOBNAME is solid as an oak!", + "%MOBNAME endured!" + ) + rollmote_fail = list( + "%MOBNAME is a floppy lil' noodle.", + "%MOBNAME is made of paper.", + "%MOBNAME would be torn to shreds by a light breeze.", + "%MOBNAME crumpled up and blew away." + ) + +/datum/statskill/charisma + key = STAT_CHA + displayname = "Charisma" + is_stat = TRUE + rollmote_triggers = list( + "mox", + "moxie", + "cha", + ) + rollmote_initial = list( + "%MOBNAME starts to be charismatic...", + "%MOBNAME puts on the charm..." + ) + rollmote_success = list( + "%MOBNAME was charismatic!", + "%MOBNAME is an absolute charmer!", + "%MOBNAME was good and charming!" + ) + rollmote_fail = list( + "%MOBNAME was totally uncharismatic.", + "%MOBNAME isn't fooling anyone.", + "%MOBNAME put %MOBTHEIR foot in %MOBTHEIR mouth.", + "%MOBNAME could hear a pin drop.", + "%MOBNAME's spaghetti is showing.", + "%MOBNAME had %MOBTHEIR charms fall flat." + ) + +/datum/statskill/intelligence + key = STAT_INT + displayname = "Intelligence" + is_stat = TRUE + rollmote_triggers = list( + "int", + "i", + "intelligence", + "inteligence", + "intelligance", + "inteligance", + "intel", + "intell", + "intelect", + "intellect", + "smart", + "smartness", + "nerd", + "nerdiness", + "dork", + "dorkiness", + "dweeb", + "dweebishness", + "smarts", + ) + rollmote_initial = list( + "%MOBNAME thinks hard...", + "%MOBNAME ponders hard...", + "%MOBNAME takes a moment to think...", + "%MOBNAME furrows %MOBTHEIR brow..." + ) + rollmote_success = list( + "%MOBNAME was clever!", + "%MOBNAME is a genius!", + "%MOBNAME has a mind sharp as a whip!", + "%MOBNAME had a thought!" + ) + rollmote_fail = list( + "%MOBNAME was dumb as a doornail.", + "%MOBNAME burned %MOBTHEIR last braincell years ago.", + "%MOBNAME is running low on braincells.", + "%MOBNAME is denser than a collapsed idiot." + ) + +/datum/statskill/agility + key = STAT_AGI + displayname = "Agility" + is_stat = TRUE + rollmote_triggers = list( + "agi", + "a", + "agility", + "agillity", + "quick", + "quickness", + "fast", + "fastness", + "dex", + "speed", + "speediness", + "initiative", + "athleticism", + "acrobatics", + "escape", + "dodge", + "evade", + "evasion", + "cat", + "deft", + "deftness", + ) + rollmote_initial = list( + "%MOBNAME tries to get agile...", + "%MOBNAME prepares %MOBTHEIR moves...", + "%MOBNAME starts to get limber..." + ) + rollmote_success = list( + "%MOBNAME was very flexible!", + "%MOBNAME had some excellent footwork!", + "%MOBNAME was in perfect control!", + "%MOBNAME was agile as a cat!", + "%MOBNAME was agile as a fox!" + ) + rollmote_fail = list( + "%MOBNAME fell flat on %MOBTHEIR face.", + "%MOBNAME fell flat on %MOBTHEIR back.", + "%MOBNAME triped over themself.", + "%MOBNAME has two left feet.", + "%MOBNAME was clumsy as a cat.", + "%MOBNAME was clumsy as a fox." + ) + +/datum/statskill/luck + key = STAT_LCK + displayname = "Luck" + is_stat = TRUE + rollmote_triggers = list( + "l", + "lck", + "luck", + "lick", + "lock", + "lunk", + "link", + "chance", + "fortune", + "dice", + "luk", + "luc", + "fat", + "fate", + ) + rollmote_initial = list( + "%MOBNAME tries %MOBTHEIR luck...", + "%MOBNAME takes a chance...", + "%MOBNAME puts it all on red..." + ) + rollmote_success = list( + "%MOBNAME lucked out!", + "%MOBNAME was the luckiest son-of-a-gun in the wasteland!", + "%MOBNAME could make a bullet turn right around and climb back into the gun!", + "%MOBNAME got lucky!" + ) + rollmote_fail = list( + "%MOBNAME was unlucky.", + "%MOBNAME realized %MOBTHEIR game was rigged from the start.", + "%MOBNAME showed that the house always wins." + ) + +/datum/statskill/basic + key = STAT_GEN + displayname = "Coinflip" + is_stat = TRUE + hidden = TRUE + rollmote_triggers = list( + "x", + "non", + "none", + "generic", + "something", + "else", + "smth", + "?", + "rand", + "huh", + "stuff", + "roll", + ) + rollmote_initial = list( + "%MOBNAME tests %MOBTHEIR skills...", + "%MOBNAME tries %MOBTHEIR skills...", + "%MOBNAME attempts to do a thing...", + "%MOBNAME puts %MOBTHEIR skills to the test..." + ) + rollmote_success = list( + "%MOBNAME succeeded!", + "%MOBNAME did it!" + ) + rollmote_fail = list( + "%MOBNAME was really bad at whatever they did.", + "%MOBNAME just really sucked.", + "%MOBNAME messed up real bad." + ) + +/datum/roll_return + var/base = 0 + var/crit_fail = FALSE + var/crit_success = FALSE + var/passed = FALSE + var/displayname = "Result" + var/mods = 0 + var/msg = "Something cool happened!" + var/resultnum = 0 + +///////////////////////////////////////////// +///////////////////////////////////////////// +/// our system of skills, attributes, and stats! +/mob/var/sas_key diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index e84eb98fe8..aa342ee004 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -18,8 +18,10 @@ for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) diag_hud.add_to_hud(src) faction += "[REF(src)]" + init_SAS() GLOB.mob_living_list += src clienthud.add_hud_to(src) + if(coolshadow) add_filter("cool_shadow",10, list( "type"="drop_shadow", @@ -67,6 +69,7 @@ ranged_ability.remove_ranged_ability(src) if(buckled) buckled.unbuckle_mob(src,force=1) + QDEL_NULL(SAS) remove_from_all_data_huds() GLOB.mob_living_list -= src diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 49b2c6e674..876528130e 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -179,3 +179,4 @@ var/icon_resting var/coolshadow = TRUE + diff --git a/code/modules/mob/living/login.dm b/code/modules/mob/living/login.dm index 3c7d8945fd..e1668395d9 100644 --- a/code/modules/mob/living/login.dm +++ b/code/modules/mob/living/login.dm @@ -31,3 +31,4 @@ hud_client_check() SSstatpanels.collect_horny_demographic(src) + SSsas. diff --git a/code/modules/mob/living/skillcheck.dm b/code/modules/mob/living/skillcheck.dm index fe0fdd95ff..a93d09605d 100644 --- a/code/modules/mob/living/skillcheck.dm +++ b/code/modules/mob/living/skillcheck.dm @@ -1,290 +1,51 @@ -#define EMOTE_SPECIAL_STR "Brawn" -#define EMOTE_SPECIAL_PER "Awareness" -#define EMOTE_SPECIAL_END "Toughness" -#define EMOTE_SPECIAL_CHA "Moxie" -#define EMOTE_SPECIAL_INT "Smarts" -#define EMOTE_SPECIAL_AGI "Deftness" -#define EMOTE_SPECIAL_LCK "Fate" -#define EMOTE_SPECIAL_GEN "Flat Roll" -#define EMOTE_SPECIAL_SUF "Crit" GLOBAL_LIST_INIT(special_skill_list, list( - EMOTE_SPECIAL_STR, - EMOTE_SPECIAL_PER, - EMOTE_SPECIAL_END, - EMOTE_SPECIAL_CHA, - EMOTE_SPECIAL_INT, - EMOTE_SPECIAL_AGI, - EMOTE_SPECIAL_LCK, - EMOTE_SPECIAL_GEN)) - -GLOBAL_LIST_INIT(special_triggers, list( - EMOTE_SPECIAL_STR = list( - "b", - "brn", - "brawn", - ), - EMOTE_SPECIAL_PER = list( - "a", - "aware", - "awareness", - ), - EMOTE_SPECIAL_END = list( - "tough", - "tuff", - "end", - "toughness", - ), - EMOTE_SPECIAL_CHA = list( - "mox", - "moxie", - "Cha", - ), - EMOTE_SPECIAL_INT = list( - "int", - "i", - "intelligence", - "inteligence", - "intelligance", - "inteligance", - "intel", - "intell", - "intelect", - "intellect", - "smart", - "smartness", - "nerd", - "nerdiness", - "dork", - "dorkiness", - "dweeb", - "dweebishness", - "smarts", - ), - EMOTE_SPECIAL_AGI = list( - "agi", - "a", - "agility", - "agillity", - "quick", - "quickness", - "fast", - "fastness", - "dex", - "speed", - "speediness", - "initiative", - "athleticism", - "acrobatics", - "escape", - "dodge", - "evade", - "evasion", - "cat", - "deft", - "deftness", - ), - EMOTE_SPECIAL_LCK = list( - "l", - "lck", - "luck", - "lick", - "lock", - "lunk", - "link", - "chance", - "fortune", - "dice", - "luk", - "luc", - "fat", - "fate", - ), - EMOTE_SPECIAL_GEN = list( - "x", - "non", - "none", - "generic", - "something", - "else", - "smth", - "?", - "rand", - "huh", - "stuff", - "roll"))) - -GLOBAL_LIST_INIT(special_phrases, list( - EMOTE_SPECIAL_STR = list( - "initial" = list( - "tests their brawn...", - "flexes their arm(s)...", - "prepares to lift...", - "puts their back into it..."), - "success" = list( - "is strong!", - "is beefy!", - "has some serious guns!", - "had some strength behind it!"), - "failure" = list( - "was too weak.", - "was a little wet noodle.", - "would loose an arm wrestling match with a mouse.", - "has some serious atrophy. it's a wonder they can move at all.")), - EMOTE_SPECIAL_PER = list( - "initial" = list( - "takes a good, long look...", - "squints...", - "looks around...", - "focuses in..."), - "success" = list( - "was perceptive!", - "noticed things!", - "has eyes like a hawk!", - "could find Doc Mitchell's keys!", - "noticed whatever they were trying to see!"), - "failure" = list( - "was totally oblivious.", - "forgot their glasses.", - "didn't see anything.")), - EMOTE_SPECIAL_END = list( - "initial" = list( - "tests their toughness...", - "braces themself..."), - "success" = list( - "was tough!", - "was one tough cookie!", - "doesn't even flinch!", - "is solid as an oak!", - "endured!"), - "failure" = list( - "is a floppy lil' noodle.", - "is made of paper.", - "would be torn to shreds by a light breeze.", - "crumpled up and blew away.")), - EMOTE_SPECIAL_CHA = list( - "initial" = list( - "starts to be charismatic...", - "puts on the charm..."), - "success" = list( - "was charismatic!", - "is an absolute charmer!", - "was good and charming!"), - "failure" = list( - "was totally uncharismatic.", - "isn't fooling anyone.", - "put their foot in their mouth.", - "could hear a pin drop.", - "miiiiight have some frontal lobe damage.", - "had their charms fall flat.")), - EMOTE_SPECIAL_INT = list( - "initial" = list( - "thinks hard...", - "ponders hard...", - "takes a moment to think...", - "furrows their brow..."), - "success" = list( - "was clever!", - "is a genius!", - "has a mind sharp as a whip!", - "had a thought!"), - "failure" = list( - "was dumb as a doornail.", - "burned their last braincell years ago.", - "is running low on braincells.", - "was dense as a brick.")), - EMOTE_SPECIAL_AGI = list( - "initial" = list( - "tries to get agile...", - "prepares their moves...", - "starts to get limber..."), - "success" = list( - "was very flexible!", - "had some excellent footwork!", - "was in perfect control!", - "was agile as a cat!", - "was agile as a fox!"), - "failure" = list( - "fell flat on their face.", - "fell flat on their back.", - "triped over themself.", - "has two left feet.", - "was clumsy as a cat.", - "was clumsy as a fox.")), - EMOTE_SPECIAL_LCK = list( - "initial" = list( - "tries their luck...", - "takes a chance...", - "puts it all on red..."), - "success" = list( - "lucked out!", - "was the luckiest son-of-a-gun in the wasteland!", - "could make a bullet turn right around and climb back into the gun!", - "got lucky!"), - "failure" = list( - "was unlucky.", - "realized their game was rigged from the start.", - "showed that the house always wins.")), - EMOTE_SPECIAL_GEN = list( - "initial" = list( - "tests their skills...", - "tries their skills...", - "attempts to do a thing...", - "puts their skills to the test..."), - "success" = list( - "succeeded!", - "did it!"), - "failure" = list( - "was really bad at whatever they did.", - "just really sucked.", - "messed up real bad.")), - EMOTE_SPECIAL_SUF = list( - "initial" = list( - "shouldnt see this lol"), - "success" = list( - "Ring-a-ding baby!", - "Wow!"), - "failure" = list( - "How could someone mess up so badly?", - "The game was rigged from the start.")))) - -/mob/living/verb/emote_special_str() + STAT_STR, + STAT_PER, + STAT_END, + STAT_CHA, + STAT_INT, + STAT_AGI, + STAT_LCK, + STAT_GEN)) + +/mob/living/verb/STAT_str() set name = "Roll Brawn" set desc = "Roll for bicep circumfrence." set category = "Roleplaying" emote("special_strength") -/mob/living/verb/emote_special_per() +/mob/living/verb/STAT_per() set name = "Roll Awareness" set desc = "Roll for eyeball circumfrence." set category = "Roleplaying" emote("special_perception") -/mob/living/verb/emote_special_end() +/mob/living/verb/STAT_end() set name = "Roll Toughness" set desc = "Roll for heart circumfrence." set category = "Roleplaying" emote("special_endurance") -/mob/living/verb/emote_special_cha() +/mob/living/verb/STAT_cha() set name = "Roll Moxie" set desc = "Roll for beauty circumfrence." set category = "Roleplaying" emote("special_charisma") -/mob/living/verb/emote_special_int() +/mob/living/verb/STAT_int() set name = "Roll Smarts" set desc = "Roll for brain circumfrence." set category = "Roleplaying" emote("special_intelligence") -/mob/living/verb/emote_special_agi() +/mob/living/verb/STAT_agi() set name = "Roll Deftness" set desc = "Roll for wiggly circumfrence." set category = "Roleplaying" emote("special_agility") -/mob/living/verb/emote_special_luc() +/mob/living/verb/STAT_luc() set name = "Roll Fate" set desc = "Roll for some sort of circumfrence." set category = "Roleplaying" @@ -302,31 +63,31 @@ GLOBAL_LIST_INIT(special_phrases, list( /datum/emote/living/special/s key = "special_strength" - special_override = EMOTE_SPECIAL_STR + special_override = STAT_STR /datum/emote/living/special/p key = "special_perception" - special_override = EMOTE_SPECIAL_PER + special_override = STAT_PER /datum/emote/living/special/e key = "special_endurance" - special_override = EMOTE_SPECIAL_END + special_override = STAT_END /datum/emote/living/special/c key = "special_charisma" - special_override = EMOTE_SPECIAL_CHA + special_override = STAT_CHA /datum/emote/living/special/i key = "special_intelligence" - special_override = EMOTE_SPECIAL_INT + special_override = STAT_INT /datum/emote/living/special/a key = "special_agility" - special_override = EMOTE_SPECIAL_AGI + special_override = STAT_AGI /datum/emote/living/special/l key = "special_luck" - special_override = EMOTE_SPECIAL_LCK + special_override = STAT_LCK /datum/emote/living/special/run_emote(mob/user, params, type_override, intentional = FALSE) if(!can_run_emote(user, TRUE, intentional)) @@ -338,54 +99,28 @@ GLOBAL_LIST_INIT(special_phrases, list( to_chat(user, "You cannot send IC messages (muted).") return FALSE - if(isnull(user.special_a)) - to_chat(user, span_phobia("You arent special.")) - to_chat(user, span_notice("Mainly because you're playing a mob withough any special skills. This is probably a bug~")) - return FALSE - - var/special_noun = null var/special_phrase_input = special_override ? lowertext(special_override) : lowertext(params) + var/datum/statskill/SK = SSsas.get_stat_by_trigger(special_phrase_input) - for(var/which_special in GLOB.special_skill_list) - /// if the thing we said after the emote is in one of these lists, pick the corresponding key - if(special_phrase_input in GLOB.special_triggers[which_special]) - special_noun = which_special - - if(!(special_noun in GLOB.special_skill_list) || !special_noun) - to_chat(user, span_alert("That's not a valid SPECIAL stat!")) + if(!SK) + to_chat(user, span_alert("That's not a valid stat!")) to_chat(user, span_notice("To use this emote, type '*special' followed by a SPECIAL stat. For instance, '*special luck' will do a (luck*10)% roll and say if you passed or not.")) - var/valid_specials - for(var/word in GLOB.special_triggers) - valid_specials += "[GLOB.special_triggers[word][1]], " - valid_specials += "[GLOB.special_triggers[word][2]]. " + var/valid_specials = SSsas.get_stat_triggers() to_chat(user, span_notice("Some of the valid SPECIAL keywords are:[valid_specials].")) return - - var/special_skill = null - switch(special_noun) - if(EMOTE_SPECIAL_STR) - special_skill = user.special_s - if(EMOTE_SPECIAL_PER) - special_skill = user.special_p - if(EMOTE_SPECIAL_END) - special_skill = user.special_e - if(EMOTE_SPECIAL_CHA) - special_skill = user.special_c - if(EMOTE_SPECIAL_INT) - special_skill = user.special_i - if(EMOTE_SPECIAL_AGI) - special_skill = user.special_a - if(EMOTE_SPECIAL_LCK) - special_skill = user.special_l - if(EMOTE_SPECIAL_GEN) // generic random 50% chance - special_skill = 5 - - var/first_phrase = pick(GLOB.special_phrases[special_noun]["initial"]) - var/message_first = span_notice("\[[special_noun], [special_skill]0%] [user] [first_phrase].") // [Luck, 100%] User tests their Luck. - - user.visible_message( + + var/init_message = SK.get_rollmote_initial_msg(user) + var/datum/roll_return/RT = SSsas.do_roll(user, SK.key, "1d20", 15) + var/modshow = "" + if(modshow > 0) + modshow = "+[RT.mods]" + if(modshow < 0) + modshow = "[RT.mods]" + var/message_first = span_notice("\[[SK.displayname], 1d20+[RT.base][modshow] DC 15] [user] [RT.pre_msg].") // [Luck, 100%] User tests their Luck. + var/turf/T = get_turf(user) + + T.visible_message( message = message_first, - self_message = message_first, blind_message = message_first) user.emote_for_ghost_sight(message_first) @@ -397,26 +132,20 @@ GLOBAL_LIST_INIT(special_phrases, list( if(!can_run_emote(user, TRUE,intentional)) return - var/message_second - if(prob(special_skill * 10)) - var/success_phrase = pick(GLOB.special_phrases[special_noun]["success"]) - if(prob(5)) // crit success! - success_phrase += " [pick(GLOB.special_phrases[EMOTE_SPECIAL_SUF]["success"])]" - message_second = span_green("\[Success\] [user] [success_phrase]") // [Success] User is pretty lucky! + var/message_second = RT.msg + if(RT.passed) + message_second = span_green(message_second) else - var/fail_phrase = pick(GLOB.special_phrases[special_noun]["failure"]) - if(prob(5)) // crit fail! - fail_phrase += " [pick(GLOB.special_phrases[EMOTE_SPECIAL_SUF]["failure"])]" - message_second = span_red("\[Failure\] [user] [fail_phrase]") // [Failure} User isn't very lucky... + message_second = span_red(span_green(message_second)) - user.visible_message( + T.visible_message( message = message_second, - self_message = message_second, blind_message = message_second) user.emote_for_ghost_sight(message_second) - +//////////////////////////////////////////////////// +//////////////////////////////////////////////////// /datum/emote/living/rollfor key = "rollfor" @@ -438,47 +167,32 @@ GLOBAL_LIST_INIT(special_phrases, list( var/paramlist = splittext(params, " ") var/skill_name - for(var/which_special in GLOB.special_skill_list) - /// if the thing we said after the emote is in one of these lists, pick the corresponding key - if(paramlist[1] in GLOB.special_triggers[which_special]) - skill_name = which_special + var/special_phrase_input = LAZYACCESS(paramlist, 1) + var/datum/statskill/SK = SSsas.get_stat_by_trigger(special_phrase_input) - if(!(skill_name in GLOB.special_skill_list) || !skill_name) - to_chat(user, span_alert("That's not a valid SPECIAL stat!")) + if(!SK) + to_chat(user, span_alert("That's not a valid stat!")) to_chat(user, span_notice("To use this emote, type '*special' followed by a SPECIAL stat. For instance, '*special luck' will do a (luck*10)% roll and say if you passed or not.")) - var/valid_specials - for(var/word in GLOB.special_triggers) - valid_specials += "[GLOB.special_triggers[word][1]], " - valid_specials += "[GLOB.special_triggers[word][2]]. " - to_chat(user, span_notice("Some of the valid SPECIAL keywords are:[valid_specials].")) + var/valid_specials = SSsas.get_stat_triggers() + to_chat(user, span_notice("Some of the valid SPECIAL keywords are:\n[valid_specials].")) return - var/skill = null - switch(skill_name) - if(EMOTE_SPECIAL_STR) - skill = user.special_s - if(EMOTE_SPECIAL_PER) - skill = user.special_p - if(EMOTE_SPECIAL_END) - skill = user.special_e - if(EMOTE_SPECIAL_CHA) - skill = user.special_c - if(EMOTE_SPECIAL_INT) - skill = user.special_i - if(EMOTE_SPECIAL_AGI) - skill = user.special_a - if(EMOTE_SPECIAL_LCK) - skill = user.special_l - if(EMOTE_SPECIAL_GEN) // flat roll - skill = 0 - var/dice = paramlist[2] - if(!roll(dice)) + if(isnull(roll(dice))) return - var/first_phrase = pick(GLOB.special_phrases[skill_name]["initial"]) - var/message_first = span_notice("\[[skill_name], [dice]+[skill]\] [user] [first_phrase].") // [Luck, 1d20+5] Player tests their luck. - user.visible_message( + var/init_message = SK.get_rollmote_initial_msg(user) + var/datum/roll_return/RT = SSsas.do_roll(user, SK.key, dice) + var/first_phrase = RT.msg_pre + var/modshow = "" + if(modshow > 0) + modshow = "+[RT.mods]" + if(modshow < 0) + modshow = "[RT.mods]" + var/turf/T = get_turf(user) + + var/message_first = span_notice("\[[SK.displayname], [dice]+[RT.base][modshow]\] [user] [first_phrase].") // [Luck, 1d20+5] Player tests their luck. + T.visible_message( message = message_first, self_message = message_first, blind_message = message_first) @@ -492,17 +206,20 @@ GLOBAL_LIST_INIT(special_phrases, list( if(!can_run_emote(user, TRUE,intentional)) return - var/result_message = span_notice("[user] rolled [roll(dice)+skill]!") - user.visible_message( + var/result_message = span_notice("[user] rolled [RT.resultnum]!") + T.visible_message( message = result_message, self_message = result_message, blind_message = result_message) user.emote_for_ghost_sight(result_message) + +////////////////////////////////////////////////// +////////////////////////////////////////////////// /mob/living/verb/suggest_skillcheck(mob/living/target) set category = "Roleplaying" set name = "Suggest Skillcheck" - var/skill = input(usr, "Which skill?", "Skill Suggester 9000", null) as null|anything in GLOB.special_skill_list + var/skill = input(usr, "Which skill?", "Skill Suggester 9000", null) as null|anything in SSsas.get_skills() if(isnull(skill)) return to_chat(target, span_info("[usr.name] would like you to perform a [skill] check.")) @@ -511,13 +228,13 @@ GLOBAL_LIST_INIT(special_phrases, list( set category = "Roleplaying" set name = "Skill Contest" // decide skill - var/skill = input(usr, "Which skill?", "Skill Chooser 9001", null) as null|anything in GLOB.special_skill_list + var/skill = input(usr, "Which skill?", "Skill Chooser 9001", null) as null|anything in SSsas.get_skills() if(isnull(skill)) return // decide the contest var/dice = input(usr, "What kind of dice? (ex: 1d20) \nIf nothing happens, you probably typed this wrong.", null) as null|text - if(!roll(dice)) + if(isnull(roll(dice))) return // get consent var/consent = alert(target, "[usr.name] would like to compete in a [dice] [skill] contest.",, "Agree", "Deny") @@ -525,51 +242,20 @@ GLOBAL_LIST_INIT(special_phrases, list( to_chat(usr, span_info("[target.name] doesn't seem like they want to participate in a contest.")) return - // run contest - var/usr_skill = 0 - var/target_skill = 0 - switch(skill) - if(EMOTE_SPECIAL_STR) - usr_skill = usr.special_s - target_skill = target.special_s - if(EMOTE_SPECIAL_PER) - usr_skill = usr.special_p - target_skill = target.special_p - if(EMOTE_SPECIAL_END) - usr_skill = usr.special_e - target_skill = target.special_e - if(EMOTE_SPECIAL_CHA) - usr_skill = usr.special_c - target_skill = target.special_c - if(EMOTE_SPECIAL_INT) - usr_skill = usr.special_i - target_skill = target.special_i - if(EMOTE_SPECIAL_AGI) - usr_skill = usr.special_a - target_skill = target.special_a - if(EMOTE_SPECIAL_LCK) - usr_skill = usr.special_l - target_skill = target.special_l - - var/usr_result = roll(dice) + usr_skill - var/target_result = roll(dice) + target_skill - - - var/winner - var/winner_result - var/loser - var/loser_result - - if(usr_result > target_result) + var/datum/statskill/SK = SSsas.get_stat_by_trigger(skill) + var/datum/return_roll/my_roll = SSsas.do_roll(usr, SK.key, dice) + var/datum/return_roll/their_roll = SSsas.do_roll(target, SK.key, dice) + + if(my_roll.resultnum > their_roll.resultnum) winner = usr - winner_result = usr_result + winner_result = my_roll.resultnum loser = target - loser_result = target_result + loser_result = their_roll.resultnum else winner = target - winner_result = target_result + winner_result = their_roll.resultnum loser = usr - loser_result = usr_result + loser_result = my_roll.resultnum to_chat(winner, span_green("You won the [skill] contest! ([winner_result] vs [loser_result])")) to_chat(loser, span_alert("You lost the [skill] contest! ([loser_result] vs [winner_result])")) diff --git a/code/modules/mob/stats_n_skills.dm b/code/modules/mob/stats_n_skills.dm new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/code/modules/mob/stats_n_skills.dm @@ -0,0 +1 @@ + diff --git a/icons/mob/screen_gen.dmi b/icons/mob/screen_gen.dmi index 2fd373ed59..26a0fb8cd6 100644 Binary files a/icons/mob/screen_gen.dmi and b/icons/mob/screen_gen.dmi differ diff --git a/modular_coyote/code/pmon_mob.dm b/modular_coyote/code/pmon_mob.dm index ca5be691ee..d2f63a3abf 100644 --- a/modular_coyote/code/pmon_mob.dm +++ b/modular_coyote/code/pmon_mob.dm @@ -38,6 +38,7 @@ dextrous_hud_type = /datum/hud/dextrous/drone //Need this to have the hands appear on the HUD held_items = list(null, null) + sas_key = null // prevent sassery ///The pokemon-types that this mob has. Used to auto-generate moves(abilities) and some other attributes. var/list/p_types = list() ///Moves that aren't automatically granted based on their type. Will be assigned during Initialize()