From cfd1900ee4d6483029d7663fd62467a9b3279737 Mon Sep 17 00:00:00 2001 From: Werner Date: Sun, 29 Apr 2018 19:34:49 +0200 Subject: [PATCH] Security Fines (#4634) Allows security to fine criminals with the sentencing console Prints a human readable copy of the charges and the applied sentence --- SQL/migrate/V026__law_database.sql | 21 ++++ code/__defines/misc.dm | 2 - code/_helpers/logging.dm | 21 ++-- code/controllers/subsystems/law.dm | 67 +++++++++++++ code/defines/procs/dbcore.dm | 2 +- code/game/machinery/computer/sentencing.dm | 111 ++++++++++++--------- code/game/machinery/machinery.dm | 7 ++ code/modules/law/incident.dm | 35 ++++--- code/modules/law/law.dm | 20 +++- html/changelogs/arrow768-fines.yml | 37 +++++++ 10 files changed, 253 insertions(+), 70 deletions(-) create mode 100644 SQL/migrate/V026__law_database.sql create mode 100644 html/changelogs/arrow768-fines.yml diff --git a/SQL/migrate/V026__law_database.sql b/SQL/migrate/V026__law_database.sql new file mode 100644 index 00000000000..b6c3a18faaa --- /dev/null +++ b/SQL/migrate/V026__law_database.sql @@ -0,0 +1,21 @@ +-- +-- Adds a new table to load the regulations ("laws") from +-- +CREATE TABLE `ss13_law` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `law_id` VARCHAR(4) NOT NULL, + `name` VARCHAR(50) NOT NULL, + `description` VARCHAR(500) NOT NULL, + `min_fine` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `max_fine` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `min_brig_time` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `max_brig_time` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `severity` INT(10) UNSIGNED NULL DEFAULT '0', + `felony` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted_at` DATETIME NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `UNIQUE LAW` (`law_id`) +) +ENGINE=InnoDB; \ No newline at end of file diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 84a43f42104..eec5b5c2140 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -278,8 +278,6 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s // Law settings #define PERMABRIG_SENTENCE 90 // Measured in minutes -//#define PERMAPRISON_SENTENCE 60 // Measured in IC days -#define FELONY_LEVEL 2.0 // What is the minimum law severity that counts as a felony? #define LAYER_TABLE 2.8 #define LAYER_UNDER_TABLE 2.79 diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index fbd6411c4e7..8bea89a5a75 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -39,6 +39,9 @@ /proc/log_debug(text,level = SEVERITY_DEBUG) if (config.log_debug) game_log("DEBUG", text) + + if (level == SEVERITY_ERROR) // Errors are always logged + error(text) for(var/client/C in admins) if(!C.prefs) //This is to avoid null.toggles runtime error while still initialyzing players preferences @@ -51,7 +54,7 @@ if (config.log_game) game_log("GAME", text) send_gelf_log( - short_message = text, + short_message = text, long_message = "[time_stamp()]: [text]", level = level, category = "GAME", @@ -92,8 +95,8 @@ if (config.log_attack) game_log("ATTACK", text) send_gelf_log( - short_message = text, - long_message = "[time_stamp()]: [text]", + short_message = text, + long_message = "[time_stamp()]: [text]", level = level, category="ATTACK", additional_data = list("_ckey" = html_encode(ckey), "_ckey_target" = html_encode(ckey_target)) @@ -108,7 +111,7 @@ if (config.log_pda) game_log("PDA", text) send_gelf_log( - short_message = text, + short_message = text, long_message = "[time_stamp()]: [text]", level = level, category="PDA", @@ -119,9 +122,9 @@ if (config.log_pda) game_log("NTIRC", text) send_gelf_log( - short_message = text, - long_message="[time_stamp()]: [text]", - level = level, + short_message = text, + long_message="[time_stamp()]: [text]", + level = level, category = "NTIRC", additional_data = list("_ckey" = html_encode(ckey), "_ntirc_conversation" = html_encode(conversation)) ) @@ -187,7 +190,7 @@ return english_list(comps, nothing_text="0", and_text="|", comma_text="|") //more or less a logging utility -/proc/key_name(var/whom, var/include_link = null, var/include_name = 1, var/highlight_special = 0, var/datum/ticket/ticket = null) +/proc/key_name(var/whom, var/include_link = null, var/include_name = 1, var/highlight_special = 0, var/datum/ticket/ticket = null) var/mob/M var/client/C var/key @@ -217,7 +220,7 @@ if(key) if(include_link && C) - . += "" + . += "" if(C && C.holder && C.holder.fakekey && !include_name) . += "Administrator" diff --git a/code/controllers/subsystems/law.dm b/code/controllers/subsystems/law.dm index a54d67df741..9f1e0fec00d 100644 --- a/code/controllers/subsystems/law.dm +++ b/code/controllers/subsystems/law.dm @@ -13,6 +13,12 @@ NEW_SS_GLOBAL(SSlaw) /datum/controller/subsystem/law/Initialize(timeofday) + if(config.sql_enabled) + load_from_sql() + else + load_from_code() + +/datum/controller/subsystem/law/proc/load_from_code() for (var/L in subtypesof(/datum/law/low_severity)) low_severity += new L @@ -23,3 +29,64 @@ high_severity += new L laws = low_severity + med_severity + high_severity + +/datum/controller/subsystem/law/proc/load_from_sql() + if(!establish_db_connection(dbcon)) + log_debug("SSlaw: SQL ERROR - Failed to connect.") + return load_from_code() + + var/DBQuery/law_query = dbcon.NewQuery("SELECT law_id, name, description, min_fine, max_fine, min_brig_time, max_brig_time, severity, felony FROM ss13_law WHERE deleted_at IS NULL ORDER BY law_id ASC") + law_query.Execute() + while(law_query.NextRow()) + CHECK_TICK + try + var/datum/law/L = new + L.id = law_query.item[1] + L.name = law_query.item[2] + L.desc = law_query.item[3] + L.min_fine = text2num(law_query.item[4]) + L.max_fine = text2num(law_query.item[5]) + L.min_brig_time = text2num(law_query.item[6]) + L.max_brig_time = text2num(law_query.item[7]) + L.severity = text2num(law_query.item[8]) + L.felony = text2num(law_query.item[9]) + + if(L.severity == 1) + low_severity += L + else if(L.severity == 2) + med_severity += L + else if(L.severity == 3) + high_severity += L + else + throw("Law with id: [L.id] deleted due to invalid severity: [L.severity]") + qdel(L) + catch(var/exception/el) + log_debug("SSlaw: Error when loading law: [el]") + + laws = low_severity + med_severity + high_severity + if(!laws.len) + log_debug("SSlaw: No laws loaded. Loading from code and migrating to SQL") + load_from_code() + migrate_to_sql() + +/datum/controller/subsystem/law/proc/migrate_to_sql() + for(var/datum/law/L in laws) + log_debug("SSlaw: Migrating law [L.id] to SQL") + var/DBQuery/law_update_query = dbcon.NewQuery({" + INSERT IGNORE INTO ss13_law + (law_id, name, description, min_fine, max_fine, min_brig_time, max_brig_time, severity, felony) + VALUES + (:law_id:, :name:, :desc:, :min_fine:, :max_fine:, :min_brig_time:, :max_brig_time:, :severity:, :felony:) + "}) + law_update_query.Execute(list( + "law_id"=L.id, + "name"=L.name, + "desc"=L.desc, + "min_fine"=L.min_fine, + "max_fine"=L.max_fine, + "min_brig_time"=L.min_brig_time, + "max_brig_time"=L.max_brig_time, + "severity"=L.severity, + "felony"=L.felony + )) + return \ No newline at end of file diff --git a/code/defines/procs/dbcore.dm b/code/defines/procs/dbcore.dm index 9c51134667a..541bf6aeae5 100644 --- a/code/defines/procs/dbcore.dm +++ b/code/defines/procs/dbcore.dm @@ -128,7 +128,7 @@ DBQuery/proc/Execute(var/list/argument_list = null, var/pass_not_found = 0, sql_ var/error = ErrorMsg() if (error) - error("SQL Error: '[error]'") + log_debug("SQL Error: '[error]'",SEVERITY_ERROR) // This is hacky and should probably be changed if (error == "MySQL server has gone away") log_game("MySQL connection drop detected, attempting to reconnect.") diff --git a/code/game/machinery/computer/sentencing.dm b/code/game/machinery/computer/sentencing.dm index 72e9ff48529..c3845c05642 100644 --- a/code/game/machinery/computer/sentencing.dm +++ b/code/game/machinery/computer/sentencing.dm @@ -162,7 +162,7 @@ . += "

" . += "
" . += "Render Guilty" - // . += "Render Guilty - Fine" + . += "Render Guilty - Fine" . += "
" return . @@ -368,8 +368,8 @@ . += "" . += "[L.name]" . += "[L.desc]" - . += "[L.min_brig_time] - [L.max_brig_time] minutes" - . += "[L.min_fine]-[L.max_fine]cR" + . += "[L.get_brig_time_string()]" + . += "[L.get_fine_string()]" . += "Charge" . += "" @@ -398,8 +398,8 @@ . += "" . += "[L.name]" . += "[L.desc]" - . += "[L.min_brig_time] - [L.max_brig_time] minutes" - . += "[L.min_fine]-[L.max_fine]cR" + . += "[L.get_brig_time_string()]" + . += "[L.get_fine_string()]" . += "Charge" . += "" @@ -427,7 +427,7 @@ . += "" . += "[L.name]" . += "[L.desc]" - . += "[L.min_brig_time] - [L.max_brig_time] minutes" + . += "[L.get_brig_time_string()]" . += "Charge" . += "" @@ -457,38 +457,66 @@ user << "[error]" return - incident.renderGuilty( user ) + print_incident_overview(incident.renderGuilty(user, 0)) var/obj/item/weapon/card/id/card = incident.card.resolve() if( incident.brig_sentence < PERMABRIG_SENTENCE) - ping( "\The [src] pings, \"[card] has been found guilty of their crimes!\"" ) + ping( "\The [src] pings, \"[card.registered_name] has been found guilty of their crimes!\"" ) else - pingx3( "\The [src] pings, \"[card] has been found guilty of their crimes and earned a HuT Sentence\"" ) + pingx3( "\The [src] pings, \"[card.registered_name] has been found guilty of their crimes and earned a HuT Sentence\"" ) incident = null menu_screen = "main_menu" -// /obj/machinery/computer/sentencing/proc/render_guilty_fine( var/mob/living/user, var/obj/item/weapon/card/id/C ) -// if( !incident ) -// user << "There is no active case!" -// return -// -// if( !istype( user )) -// return -// -// var/error = print_incident_report() -// -// if( error ) -// user << "[error]" -// return -// -// //TODO: Try to charge the criminal if not return and print error message -// -// incident.renderGuilty( user ) -// -// ping( "\The [src] pings, \"[incident.card] has been fined for their crimes!\"" ) -// -// incident = null -// menu_screen = "main_menu" +/obj/machinery/computer/sentencing/proc/render_guilty_fine( var/mob/living/user ) + if(!incident) + user << "There is no active case!" + return + + if(!istype(user)) + return + + if(incident.fine <= 0) + buzz("\The [src] buzzes, \"No fine has been entered!\"") + return + + //Lets check if there is a felony amongst the crimes + for(var/datum/law/L in incident.charges) + if(L.felony) + buzz("\The [src] buzzes, \"The crimes are too severe to apply a fine!\"") + if(!L.can_fine()) + buzz("\The [src] buzzes, \"It is not possible to fine for [L.name]\"") + return + + //Try to resole the security account first + var/datum/money_account/security_account = department_accounts["Security"] + if(!security_account) + buzz("\The [src] buzzes, \"Could not get security account!\"") + return + + var/obj/item/weapon/card/id/card = incident.card.resolve() + //Let´s get the account of the suspect and verify they have enough money + var/datum/money_account/suspect_account = get_account(card.associated_account_number) + if(!suspect_account) + buzz("\The [src] buzzes, \"Could not get suspect account!\"") + return + + if(suspect_account.money < incident.fine) + buzz("\The [src] buzzes, \"There is not enough money in the account to pay the fine!\"") + return + + charge_to_account(suspect_account.account_number,security_account.owner_name,"Incident: [incident.UID]","Sentencing Console",-incident.fine) + charge_to_account(security_account.account_number,suspect_account.owner_name,"Incident: [incident.UID]Fine","Sentencing Console",incident.fine) + print_incident_overview(incident.renderGuilty(user,1)) + + ping("\The [src] pings, \"[card.registered_name] has been fined for their crimes!\"") + + incident = null + menu_screen = "main_menu" + +/obj/machinery/computer/sentencing/proc/print_incident_overview(var/text) + var/obj/item/weapon/paper/P = new /obj/item/weapon/paper + P.set_content_unsafe("Incident Summary",text) + print(P) /obj/machinery/computer/sentencing/proc/print_incident_report( var/sentence = 1 ) var/error = incident.missingSentenceReq() @@ -636,20 +664,13 @@ if( alert("No incident notes were added. Adding a short description of the incident is highly recommended. Do you still want to continue with the print?",,"Yes","No") == "No" ) return render_guilty( usr ) - // if( "render_guilty_fine") - // //Check for the notes - // if( !incident.notes ) - // if( alert("No incident notes were added. Adding a short description of the incident is highly recommended. Do you still want to continue with the print?",,"Yes","No") == "No" ) - // return - // //Get the ID Card - // var/obj/item/weapon/card/id/C = usr.get_active_hand() - // if( istype( C )) - // if( incident && C.mob ) - // render_guilty_fine( usr, C) - // else - // usr << "\The [src] buzzes, \"ID card not tied to a NanoTrasen Employee!\"" - // else - // usr << "\The [src] buzzes, \"The suspects ID is required to fine them\"" + if( "render_guilty_fine" ) + //Check for the notes + if( !incident.notes ) + if( alert("No incident notes were added. Adding a short description of the incident is highly recommended. Do you still want to continue with the print?",,"Yes","No") == "No" ) + return + //Get the ID Card + render_guilty_fine( usr ) add_fingerprint(usr) updateUsrDialog() diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index bd6941952ff..97f31919a7e 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -296,6 +296,13 @@ Class Procs: state(text, "blue") playsound(src.loc, 'sound/machines/pingx3.ogg', 50, 0) +/obj/machinery/proc/buzz(text=null) + if (!text) + text = "\The [src] buzzes." + + state(text, "blue") + playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0) //TODO: Check if that one is the correct sound + /obj/machinery/proc/shock(mob/user, prb) if(inoperable()) return 0 diff --git a/code/modules/law/incident.dm b/code/modules/law/incident.dm index 3b02dbc1f11..c3e592dc09f 100644 --- a/code/modules/law/incident.dm +++ b/code/modules/law/incident.dm @@ -134,14 +134,25 @@ return max -/datum/crime_incident/proc/renderGuilty( var/mob/living/user ) +//type: 0 - brig sentence, 1 - fine, 2 - prison sentence +/datum/crime_incident/proc/renderGuilty( var/mob/living/user, var/type=0 ) if( !criminal ) return created_by = "[user.ckey] - [user.real_name]" + if(type == 0) + prison_sentence = 0 + fine = 0 + else if(type == 1) + brig_sentence = 0 + prison_sentence = 0 + else if(type == 2) + brig_sentence = 0 + fine = 0 + saveCharInfraction() - generateReport() + return generateReport() /datum/crime_incident/proc/generateReport() . = "
Security Incident Report

" @@ -149,18 +160,18 @@ . += "
" . += "CRIMINAL: [criminal]

" - . += "[criminal] was found guilty of the following crimes on [time2text(world.realtime, "DD/MMM")]/[game_year]. " + . += "[criminal] was found guilty of the following crimes on [game_year]-[time2text(world.realtime, "MMM-DD")].
" - if( !brig_sentence && !prison_sentence ) - . += "As decided by the arbiter(s), they will serve no time for their crimes." - else + if( brig_sentence != 0 ) . += "As decided by the arbiter(s), they will serve the following sentence:
" - if( brig_sentence ) - if( brig_sentence >= PERMABRIG_SENTENCE ) - . += "\tBRIG: Holding until transfer
" - else - . += "\tBRIG: [brig_sentence] minutes
" - + if( brig_sentence >= PERMABRIG_SENTENCE ) + . += "\tBRIG: Holding until transfer
" + else + . += "\tBRIG: [brig_sentence] minutes
" + else if ( fine != 0 ) + . += "As decided by the arbiter(s), they have been fined [fine] credits.
" + else + . += "As decided by the arbiter(s), they will serve no time for their crimes.
" . += "
" . += "" for( var/datum/law/L in charges ) diff --git a/code/modules/law/law.dm b/code/modules/law/law.dm index 042922915fb..de2fb1732a9 100644 --- a/code/modules/law/law.dm +++ b/code/modules/law/law.dm @@ -30,4 +30,22 @@ $PRISONER_NAME was found guilty of $CRIME on $DATE. Their sentence was $SENTENCE var/max_prison_time = 0 A sentence totalling 60 days or more is a life sentence*/ var/severity = 0 // 1 - Low, 2 - Medium, 3 - High - var/felony = 0 // Does this law carry a felony conviction? \ No newline at end of file + var/felony = 0 // Does this law carry a felony conviction? + +/datum/law/proc/can_fine() + if(max_fine == 0) + return FALSE + return TRUE + +/datum/law/proc/get_fine_string() + if(max_fine == 0) + return "n.a." + return "[min_fine] - [max_fine] cR" + +/datum/law/proc/get_brig_time_string() + if(min_brig_time >= PERMABRIG_SENTENCE) + return "HuT" + if(max_brig_time >= PERMABRIG_SENTENCE) + return "[min_brig_time] minutes - HuT" + else + return "[min_brig_time] - [max_brig_time] minutes" \ No newline at end of file diff --git a/html/changelogs/arrow768-fines.yml b/html/changelogs/arrow768-fines.yml new file mode 100644 index 00000000000..05ab4221ee8 --- /dev/null +++ b/html/changelogs/arrow768-fines.yml @@ -0,0 +1,37 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +# balance +################################# + +# Your name. +author: Arrow768 + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "The sentencing console has been upgraded with a integrated fining system."
CHARGES