diff --git a/SQL/migrate/V040__CustomSynths.sql b/SQL/migrate/V040__CustomSynths.sql
new file mode 100644
index 00000000000..9891ccaff6f
--- /dev/null
+++ b/SQL/migrate/V040__CustomSynths.sql
@@ -0,0 +1,16 @@
+--
+-- Create the Custom Synths database.
+--
+
+CREATE TABLE `ss13_customsynths` (
+ `synthname` VARCHAR(128) NOT NULL,
+ `synthckey` varchar(32) NOT NULL,
+ `synthicon` VARCHAR(26) NOT NULL,
+ `aichassisicon` VARCHAR(100) NOT NULL,
+ `aiholoicon` VARCHAR(100) NOT NULL,
+ `paiicon` VARCHAR(100) NOT NULL,
+ PRIMARY KEY (`synthname`),
+ CONSTRAINT `fk_ss13_custom_synths_ss13_players` FOREIGN KEY (`synthckey`) REFERENCES `ss13_player` (`ckey`) ON DELETE CASCADE ON UPDATE CASCADE
+)
+ENGINE=InnoDB
+;
\ No newline at end of file
diff --git a/aurorastation.dme b/aurorastation.dme
index faacde8d034..6051bce61e3 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -299,6 +299,7 @@
#include "code\datums\helper_datums\events.dm"
#include "code\datums\helper_datums\getrev.dm"
#include "code\datums\helper_datums\space_ruin.dm"
+#include "code\datums\helper_datums\synthsprites.dm"
#include "code\datums\helper_datums\teleport.dm"
#include "code\datums\helper_datums\topic_input.dm"
#include "code\datums\looping_sounds\_looping_sound.dm"
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 43c3a3214ac..475dc62379a 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -286,6 +286,7 @@ var/list/gamemode_cache = list()
var/rounds_until_hard_restart = -1 // Changes how often a hard restart will be executed.
var/docs_load_docs_from
+ var/load_customsynths_from
var/docs_image_host
var/ert_base_chance = 10
@@ -899,6 +900,8 @@ var/list/gamemode_cache = list()
if ("docs_load_docs_from")
docs_load_docs_from = value
+ if ("load_customsynths_from")
+ load_customsynths_from = value
if ("docs_image_host")
docs_image_host = value
diff --git a/code/datums/helper_datums/synthsprites.dm b/code/datums/helper_datums/synthsprites.dm
new file mode 100644
index 00000000000..af02979e1b7
--- /dev/null
+++ b/code/datums/helper_datums/synthsprites.dm
@@ -0,0 +1,65 @@
+
+/*
+
+Just some quick documentation about how this works
+
+Synthckey is the players ckey which is represented under the synths name in the json, this is required to have
+synthicon is just the sprites name in the dmi
+aichassisicon is the ai chassis icon sprite name
+aiholoicon is the ai hologram icon sprite name
+paiicon is the pai icon sprite name
+
+*/
+
+
+
+/datum/custom_synth
+ var/synthname = ""
+ var/synthckey = ""
+ var/synthicon = "robot"
+ var/aichassisicon = ""
+ var/aiholoicon = ""
+ var/paiicon = ""
+
+
+
+/proc/loadsynths_from_json()
+ var/list/customsynthsprites = list()
+ try
+ customsynthsprites = json_decode(return_file_text("config/customsynths.json"))
+ catch(var/exception/ej)
+ log_debug("Error: Warning: Could not load custom synth config, as customsynths.json is missing - [ej]")
+ return
+
+ robot_custom_icons = list()
+ customsynthsprites = customsynthsprites["synths"]
+ for (var/synthsprite in customsynthsprites)
+ var/datum/custom_synth/synth = new()
+ synth.synthname = synthsprite
+ synth.synthckey = customsynthsprites[synthsprite]["ckey"]
+ synth.synthicon = customsynthsprites[synthsprite]["synthicon"]
+ synth.aichassisicon = customsynthsprites[synthsprite]["aichassisicon"]
+ synth.aiholoicon = customsynthsprites[synthsprite]["aiholoicon"]
+ synth.paiicon = customsynthsprites[synthsprite]["paiicon"]
+ robot_custom_icons[synthsprite] = synth
+
+/proc/loadsynths_from_sql()
+ if(!establish_db_connection(dbcon))
+ log_debug("SQL ERROR - Failed to connect. - Falling back to JSON")
+ loadsynths_from_json()
+ return
+ else
+
+ var/DBQuery/customsynthsprites = dbcon.NewQuery("SELECT synthname, synthckey, synthicon, aichassisicon, aiholoicon, paiicon FROM ss13_customsynths WHERE deleted_at IS NULL ORDER BY synthckey ASC")
+ customsynthsprites.Execute()
+ while(customsynthsprites.NextRow())
+ CHECK_TICK
+
+ var/datum/custom_synth/synth = new()
+ synth.synthname = customsynthsprites.item[1]
+ synth.synthckey = customsynthsprites.item[2]
+ synth.synthicon = customsynthsprites.item[3]
+ synth.aichassisicon = customsynthsprites.item[4]
+ synth.aiholoicon = customsynthsprites.item[5]
+ synth.paiicon = customsynthsprites.item[6]
+ robot_custom_icons[synth.synthname] = synth
diff --git a/code/modules/mob/holder.dm b/code/modules/mob/holder.dm
index 685d1276b74..5361409773e 100644
--- a/code/modules/mob/holder.dm
+++ b/code/modules/mob/holder.dm
@@ -592,6 +592,19 @@ var/list/holder_mob_icon_cache = list()
/obj/item/weapon/holder/pai/rabbit
icon_state = "rabbit_rest"
item_state = "rabbit"
+/obj/item/weapon/holder/pai/custom
+ var/customsprite = 1
+
+/obj/item/weapon/holder/pai/custom/sync(mob/living/M)
+ ..()
+ set_paiholder()
+
+/obj/item/weapon/holder/pai/custom/proc/set_paiholder()
+
+ if(contained && customsprite == 1)
+ icon = CUSTOM_ITEM_SYNTH
+ icon_state = "[contained.icon_state]-holder"
+ item_state = "[contained.icon_state]"
//corgi
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 12c8a8e27fc..1870a287ae7 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -87,7 +87,7 @@ var/list/ai_verbs_default = list(
var/list/cameraRecords = list() //For storing what is shown to the cameras
var/datum/ai_icon/selected_sprite // The selected icon set
- var/custom_sprite = 0 // Whether the selected icon is custom
+ var/custom_sprite = FALSE // Whether the selected icon is custom
var/carded
var/multitool_mode = 0
@@ -245,24 +245,13 @@ var/list/ai_verbs_default = list(
return ..()
/mob/living/silicon/ai/proc/setup_icon()
- var/file = file2text("config/custom_sprites.txt")
- var/lines = text2list(file, "\n")
-
- for(var/line in lines)
- // split & clean up
- var/list/Entry = text2list(line, ":")
- for(var/i = 1 to Entry.len)
- Entry[i] = trim(Entry[i])
-
- if(Entry.len < 2)
- continue;
-
- if(Entry[1] == src.ckey && Entry[2] == src.real_name)
- icon = CUSTOM_ITEM_SYNTH
- custom_sprite = 1
- selected_sprite = new/datum/ai_icon("Custom", "[src.ckey]-ai", "4", "[ckey]-ai-crash", "#FFFFFF", "#FFFFFF", "#FFFFFF")
- else
- selected_sprite = default_ai_icon
+ var/datum/custom_synth/sprite = robot_custom_icons[name]
+ if(istype(sprite) && sprite.synthckey == ckey)
+ custom_sprite = TRUE
+ icon = CUSTOM_ITEM_SYNTH
+ selected_sprite = new/datum/ai_icon("Custom", "[sprite.aichassisicon]", "4", "[sprite.aichassisicon]-crash", "#FFFFFF", "#FFFFFF", "#FFFFFF")
+ else
+ selected_sprite = default_ai_icon
updateicon()
/mob/living/silicon/ai/pointed(atom/A as mob|obj|turf in view())
@@ -634,7 +623,8 @@ var/list/ai_verbs_default = list(
var/icon_list[] = list(
"default",
"floating face",
- "carp"
+ "carp",
+ "custom"
)
input = input("Please select a hologram:") as null|anything in icon_list
if(input)
@@ -646,6 +636,14 @@ var/list/ai_verbs_default = list(
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo2"))
if("carp")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo4"))
+ if("custom")
+ if(custom_sprite)
+ var/datum/custom_synth/sprite = robot_custom_icons[name]
+ if(istype(sprite) && sprite.synthckey == ckey && sprite.aiholoicon)
+ holo_icon = getHologramIcon(icon("icons/mob/custom_synths/customhologram.dmi","[sprite.aiholoicon]"))
+ else
+ to_chat(src, "You do not have a custom sprite!")
+ return
return
//Toggles the luminosity and applies it by re-entereing the camera.
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index 73685dfd2c7..f3952fa57d0 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -18,6 +18,7 @@
var/obj/item/device/paicard/card // The card we inhabit
var/obj/item/device/radio/radio // Our primary radio
+
var/chassis = "repairbot" // A record of your chosen chassis.
var/global/list/possible_chassis = list(
"Drone" = "repairbot",
@@ -25,6 +26,7 @@
"Rat" = "rat",
"Monkey" = "monkey",
"Rabbit" = "rabbit"
+
)
var/global/list/pai_holder_types = list(
@@ -131,6 +133,7 @@
add_language(LANGUAGE_TRADEBAND, 1)
add_language(LANGUAGE_GUTTER, 1)
add_language(LANGUAGE_EAL, 1)
+ set_custom_sprite()
verbs += /mob/living/silicon/pai/proc/choose_chassis
verbs += /mob/living/silicon/pai/proc/choose_verbs
@@ -146,6 +149,15 @@
pda.name = "[pda.owner] ([pda.ownjob])"
pda.toff = TRUE
+
+/mob/living/silicon/pai/proc/set_custom_sprite()
+ var/datum/custom_synth/sprite = robot_custom_icons[name]
+ if(istype(sprite) && sprite.synthckey == ckey)
+ possible_chassis["Custom"] = "[sprite.paiicon]"
+ pai_holder_types["Custom"] = /obj/item/weapon/holder/pai/custom
+ icon = CUSTOM_ITEM_SYNTH
+ else
+ return
/mob/living/silicon/pai/init_id()
. = ..()
idcard.registered_name = ""
@@ -361,7 +373,7 @@
var/choice
var/finalized = "No"
while(finalized == "No" && src.client)
-
+ set_custom_sprite()
choice = input(usr,"What would you like to use for your mobile chassis icon? This decision can only be made once.") as null|anything in possible_chassis
if(!choice) return
diff --git a/code/modules/mob/living/silicon/robot/custom_sprites.dm b/code/modules/mob/living/silicon/robot/custom_sprites.dm
index 737b01ef318..ba1fe2a4a3e 100644
--- a/code/modules/mob/living/silicon/robot/custom_sprites.dm
+++ b/code/modules/mob/living/silicon/robot/custom_sprites.dm
@@ -3,33 +3,21 @@
//Since the ckey is used as the icon_state, the current system will only permit a single custom robot sprite per ckey.
//While it might be possible for a ckey to use that custom sprite for several real_names, it seems rather pointless to support it.
var/list/robot_custom_icons
-
/hook/startup/proc/load_robot_custom_sprites()
- var/config_file = file2text("config/custom_sprites.txt")
- var/list/lines = text2list(config_file, "\n")
-
- robot_custom_icons = list()
- for(var/line in lines)
- //split entry into ckey and real_name
- var/split_idx = findtext(line, "-") //this works if ckey cannot contain dashes, and findtext starts from the beginning
- if(!split_idx || split_idx == length(line))
- continue //bad entry
-
- var/ckey = copytext(line, 1, split_idx)
- var/real_name = copytext(line, split_idx+1)
-
- robot_custom_icons[ckey] = real_name
+ if(config.load_customsynths_from == "sql")
+ loadsynths_from_sql()
+ else if(config.load_customsynths_from == "json")
+ loadsynths_from_json()
return 1
-
/mob/living/silicon/robot/proc/set_custom_sprite()
- var/rname = robot_custom_icons[ckey]
- if(rname && rname == real_name)
+ var/datum/custom_synth/sprite = robot_custom_icons[name]
+ if(istype(sprite) && sprite.synthckey == ckey)
custom_sprite = 1
icon = CUSTOM_ITEM_SYNTH
var/list/valid_states = icon_states(icon)
if(icon_state == "robot")
- if("[ckey]-Standard" in valid_states)
- icon_state = "[ckey]-Standard"
+ if("[sprite.synthicon]-Standard" in valid_states)
+ icon_state = "[sprite.synthicon]-Standard"
else
- to_chat(src, "Could not locate [ckey]-Standard sprite.")
- icon = 'icons/mob/robots.dmi'
+ to_chat(src, "Could not locate [sprite.synthicon]-Standard sprite.")
+ icon = 'icons/mob/robots.dmi'
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index b3bd95cba24..80b8bfa9fe9 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -267,15 +267,16 @@
//Custom_sprite check and entry
if (custom_sprite == 1)
+ var/datum/custom_synth/sprite = robot_custom_icons[name]
var/list/valid_states = icon_states(CUSTOM_ITEM_SYNTH)
- if("[ckey]-[modtype]" in valid_states)
- module_sprites["Custom"] = "[src.ckey]-[modtype]"
+ if("[sprite.synthicon]-[modtype]" in valid_states)
+ module_sprites["Custom"] = "[sprite.synthicon]-[modtype]"
icon = CUSTOM_ITEM_SYNTH
icontype = "Custom"
else
icontype = module_sprites[1]
icon = 'icons/mob/robots.dmi'
- to_chat(src, "Custom Sprite Sheet does not contain a valid icon_state for [ckey]-[modtype]")
+ to_chat(src, "Custom Sprite Sheet does not contain a valid icon_state for [sprite.synthicon]-[modtype]")
else
icontype = module_sprites[1]
icon_state = module_sprites[icontype]
diff --git a/config/example/customsynths.json b/config/example/customsynths.json
new file mode 100644
index 00000000000..74869bf7715
--- /dev/null
+++ b/config/example/customsynths.json
@@ -0,0 +1,12 @@
+{
+ "synths": {
+ "Synth name here": {
+ "ckey": "ckey",
+ "synthicon": "synth icon name",
+ "aiholoicon": "ai holo icon name",
+ "aichassisicon": "ai chassis icon name",
+ "paiicon": "pai icon name"
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/html/changelogs/customsynths.yml b/html/changelogs/customsynths.yml
new file mode 100644
index 00000000000..3c4b75fc414
--- /dev/null
+++ b/html/changelogs/customsynths.yml
@@ -0,0 +1,41 @@
+################################
+# 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
+# admin
+# backend
+# security
+# refactor
+#################################
+
+# Your name.
+author: Lady Fowl
+
+# 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:
+ - refactor: "Added support for multiple synthetic sprites, aswell as custom pai's and holograms."
diff --git a/icons/mob/custom_synthetic.dmi b/icons/mob/custom_synthetic.dmi
index fe4109e8081..38e9d020ac4 100644
Binary files a/icons/mob/custom_synthetic.dmi and b/icons/mob/custom_synthetic.dmi differ
diff --git a/icons/mob/custom_synths/customhologram.dmi b/icons/mob/custom_synths/customhologram.dmi
new file mode 100644
index 00000000000..c644196bb23
Binary files /dev/null and b/icons/mob/custom_synths/customhologram.dmi differ