mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-20 12:35:33 +01:00
[MIRROR] Refactors and improves SDQL spells (#7854)
* Refactors and improves SDQL spells (#60972) SDQL spells have been improved in several ways: - The behavior of executing SDQL spell queries using datums has been migrated to a component - Projectile SDQL spells can now fire any subtype of /obj/projectile - Touch SDQL spells can use any subtype of /obj/item/melee/touch_attack - More robust parse error detection - Parse errors while loading a file from json are displayed in a modal window, with the option to load whatever parts of the spell were correctly parsed (be forewarned, scrollable sections are scuffed, and the ones that appear in the parse error modal can only be scrolled through by drag-selecting the text within them) - Fixes a bug with the names of variables within lists - Compartmentalizes SDQL spell code into several .dm files. Co-authored-by: Aleksej Komarov <stylemistake@ gmail.com> * Refactors and improves SDQL spells Co-authored-by: Y0SH1M4S73R <legoboyo@earthlink.net> Co-authored-by: Aleksej Komarov <stylemistake@ gmail.com>
This commit is contained in:
@@ -90,6 +90,7 @@ GLOBAL_PROTECT(admin_verbs_admin)
|
||||
/datum/admins/proc/togglesooc, //SKYRAT EDIT ADDITION - ADMIN
|
||||
/datum/admins/proc/open_borgopanel,
|
||||
/datum/admins/proc/view_all_circuits,
|
||||
/datum/admins/proc/view_all_sdql_spells,
|
||||
)
|
||||
GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/ban_panel, /client/proc/stickybanpanel))
|
||||
GLOBAL_PROTECT(admin_verbs_ban)
|
||||
@@ -204,7 +205,7 @@ GLOBAL_PROTECT(admin_verbs_debug)
|
||||
/datum/admins/proc/create_or_modify_area,
|
||||
/client/proc/check_timer_sources,
|
||||
/client/proc/toggle_cdn,
|
||||
/client/proc/cmd_give_sdql_spell,
|
||||
/client/proc/cmd_sdql_spell_menu,
|
||||
/client/proc/adventure_manager,
|
||||
/client/proc/load_circuit,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/datum/component/sdql_executor
|
||||
var/query = "CALL visible_message(\"<span class='warning'>The spell fizzles!</span>\") ON * IN TARGETS"
|
||||
var/giver //The ckey of the user that gave this spell
|
||||
var/list/scratchpad = list() //Use this to store vars in between queries and casts.
|
||||
var/list/saved_overrides = list()
|
||||
|
||||
/datum/component/sdql_executor/Initialize(giver)
|
||||
src.giver = giver
|
||||
|
||||
//Returns the address of x without the square brackets around it.
|
||||
#define RAW_ADDRESS(x) copytext("\ref[x]",2,-1)
|
||||
|
||||
/datum/component/sdql_executor/proc/execute(list/targets, mob/user)
|
||||
if(!CONFIG_GET(flag/sdql_spells))
|
||||
return
|
||||
if(!length(query))
|
||||
return
|
||||
var/query_text = query
|
||||
var/message_query = query
|
||||
var/list/targets_and_user_list = list(user)
|
||||
if(targets)
|
||||
targets_and_user_list += targets
|
||||
var/targets_and_user_string = ref_list(targets_and_user_list)
|
||||
var/targets_string = ref_list(targets)
|
||||
query_text = replacetextEx_char(query_text, "TARGETS_AND_USER", "[targets_and_user_string]")
|
||||
message_query = replacetext_char(message_query, "TARGETS_AND_USER", (targets_and_user_list.len > 3) ? "\[<i>[targets_and_user_list.len] items</i>]" : targets_and_user_string)
|
||||
query_text = replacetextEx_char(query_text, "USER", "{[RAW_ADDRESS(user)]}")
|
||||
message_query = replacetextEx_char(message_query, "USER", "{[RAW_ADDRESS(user)]}")
|
||||
query_text = replacetextEx_char(query_text, "TARGETS", "[targets_string]")
|
||||
message_query = replacetextEx_char(message_query, "TARGETS", (targets?.len > 3) ? "\[<i>[targets.len] items</i>]" : targets_string)
|
||||
query_text = replacetextEx_char(query_text, "SOURCE", "{[RAW_ADDRESS(parent)]}")
|
||||
message_query = replacetextEx_char(message_query, "SOURCE", "{[RAW_ADDRESS(parent)]}")
|
||||
query_text = replacetextEx_char(query_text, "SCRATCHPAD", "({[RAW_ADDRESS(src)]}.scratchpad)")
|
||||
message_query = replacetextEx_char(message_query, "SCRATCHPAD", "({[RAW_ADDRESS(src)]}.scratchpad)")
|
||||
if(!usr) //We need to set AdminProcCaller manually because it won't be set automatically by WrapAdminProcCall if usr is null
|
||||
GLOB.AdminProcCaller = "SDQL_SPELL_OF_[user.ckey]"
|
||||
GLOB.AdminProcCallCount++
|
||||
world.SDQL2_query(query_text, "[key_name(user, TRUE)] (via an SDQL spell given by [giver])", "[key_name(user)] (via an SDQL spell given by [giver])")
|
||||
GLOB.AdminProcCallCount--
|
||||
GLOB.AdminProcCaller = null
|
||||
|
||||
/datum/component/sdql_executor/proc/ref_list(list/L)
|
||||
if(isnull(L) || !L.len)
|
||||
return "\[]"
|
||||
var/ret = "\["
|
||||
for(var/i in 1 to L.len-1)
|
||||
ret += "{[RAW_ADDRESS(L[i])]},"
|
||||
ret += "{[RAW_ADDRESS(L[L.len])]}]"
|
||||
return ret
|
||||
|
||||
#undef RAW_ADDRESS
|
||||
@@ -0,0 +1,77 @@
|
||||
/// An admin verb to view all sdql spells, plus useful information
|
||||
/datum/admins/proc/view_all_sdql_spells()
|
||||
set category = "Admin.Game"
|
||||
set name = "View All SDQL Spells"
|
||||
|
||||
if(CONFIG_GET(flag/sdql_spells) || tgui_alert(usr, "SDQL spells are disabled. Open the admin panel anyways?", "SDQL Admin Panel", list("Yes", "No")) == "Yes")
|
||||
var/static/datum/SDQL_spell_panel/SDQL_spell_panel = new
|
||||
SDQL_spell_panel.ui_interact(usr)
|
||||
|
||||
/datum/SDQL_spell_panel
|
||||
|
||||
/datum/SDQL_spell_panel/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["spells"] = list()
|
||||
|
||||
for (var/obj/effect/proc_holder/spell/spell as anything in GLOB.sdql_spells)
|
||||
var/mob/living/owner = spell.owner.resolve()
|
||||
var/datum/component/sdql_executor/executor = spell.GetComponent(/datum/component/sdql_executor)
|
||||
if(!executor)
|
||||
continue
|
||||
|
||||
data["spells"] += list(list(
|
||||
"ref" = REF(spell),
|
||||
"name" = "[spell]",
|
||||
"owner" = owner,
|
||||
"ownerRef" = REF(owner),
|
||||
"creator" = executor.giver
|
||||
))
|
||||
|
||||
return data
|
||||
|
||||
/datum/SDQL_spell_panel/ui_act(action, list/params)
|
||||
. = ..()
|
||||
if (.)
|
||||
return .
|
||||
|
||||
switch(action)
|
||||
if("edit_spell")
|
||||
var/obj/effect/proc_holder/spell/spell = locate(params["spell"])
|
||||
if(!spell)
|
||||
to_chat(usr, span_warning("That spell no longer exists!"))
|
||||
return
|
||||
var/datum/component/sdql_executor/executor = spell.GetComponent(/datum/component/sdql_executor)
|
||||
if(!executor)
|
||||
to_chat(usr, span_warning("[spell][spell.p_s()] SDQL executor component is gone!"))
|
||||
return
|
||||
if(usr.ckey == executor.giver || tgui_alert(usr, "You didn't create this SDQL spell. Edit it anyways?", "SDQL Admin Panel", list("Yes", "No")) == "Yes")
|
||||
usr.client?.cmd_sdql_spell_menu(spell)
|
||||
if("follow_owner")
|
||||
var/mob/living/owner = locate(params["owner"])
|
||||
if(!owner)
|
||||
to_chat(usr, span_warning("That mob no longer exists!"))
|
||||
return
|
||||
usr.client?.admin_follow(owner)
|
||||
if("vv_spell")
|
||||
var/obj/effect/proc_holder/spell/spell = locate(params["spell"])
|
||||
if(!spell)
|
||||
to_chat(usr, span_warning("That spell no longer exists!"))
|
||||
return
|
||||
usr.client?.debug_variables(spell)
|
||||
if("open_player_panel")
|
||||
var/mob/living/owner = locate(params["owner"])
|
||||
if(!owner)
|
||||
to_chat(usr, span_warning("That mob no longer exists!"))
|
||||
return
|
||||
usr.client?.holder?.show_player_panel(owner)
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/SDQL_spell_panel/ui_state(mob/user)
|
||||
return GLOB.admin_state
|
||||
|
||||
/datum/SDQL_spell_panel/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "SDQLSpellAdminPanel")
|
||||
ui.open()
|
||||
@@ -0,0 +1,921 @@
|
||||
GLOBAL_LIST_INIT_TYPED(sdql_spells, /obj/effect/proc_holder/spell, list())
|
||||
|
||||
/client/proc/cmd_sdql_spell_menu(target in GLOB.mob_list)
|
||||
set name = "Give/Edit SDQL spell"
|
||||
set hidden = TRUE
|
||||
if(CONFIG_GET(flag/sdql_spells))
|
||||
var/datum/give_sdql_spell/ui = new(usr, target)
|
||||
ui.ui_interact(usr)
|
||||
else
|
||||
to_chat(usr, span_warning("SDQL spells are disabled."))
|
||||
|
||||
/datum/give_sdql_spell
|
||||
var/client/user
|
||||
var/mob/living/target_mob
|
||||
var/obj/effect/proc_holder/spell/target_spell
|
||||
var/spell_type
|
||||
var/list/saved_vars = list("query" = "")
|
||||
var/list/list_vars = list()
|
||||
var/list/parse_result = null
|
||||
var/alert
|
||||
|
||||
//This list contains all the vars that it should be okay to edit from the menu
|
||||
var/static/list/editable_spell_vars = list(
|
||||
"action_background_icon_state",
|
||||
"action_icon_state",
|
||||
"action_icon",
|
||||
"active_msg",
|
||||
"aim_assist",
|
||||
"antimagic_allowed",
|
||||
"base_icon_state",
|
||||
"centcom_cancast",
|
||||
"charge_max",
|
||||
"charge_type",
|
||||
"clothes_req",
|
||||
"cone_level",
|
||||
"cult_req",
|
||||
"deactive_msg",
|
||||
"desc",
|
||||
"drawmessage",
|
||||
"dropmessage",
|
||||
"hand_path",
|
||||
"hand_var_overrides",
|
||||
"holder_var_amount",
|
||||
"holder_var_type",
|
||||
"human_req",
|
||||
"include_user",
|
||||
"inner_radius",
|
||||
"invocation_emote_self",
|
||||
"invocation_type",
|
||||
"invocation",
|
||||
"max_targets",
|
||||
"message",
|
||||
"name",
|
||||
"nonabstract_req",
|
||||
"overlay_icon_state",
|
||||
"overlay_icon",
|
||||
"overlay_lifespan",
|
||||
"overlay",
|
||||
"phase_allowed",
|
||||
"player_lock",
|
||||
"projectile_type",
|
||||
"projectile_amount",
|
||||
"projectile_var_overrides",
|
||||
"projectiles_per_fire",
|
||||
"random_target_priority",
|
||||
"random_target",
|
||||
"range",
|
||||
"ranged_mousepointer",
|
||||
"respect_density",
|
||||
"selection_type",
|
||||
"self_castable",
|
||||
"smoke_amt",
|
||||
"smoke_spread",
|
||||
"sound",
|
||||
"sparks_amt",
|
||||
"sparks_spread",
|
||||
"stat_allowed",
|
||||
"still_recharging_msg",
|
||||
"target_ignore_prev",
|
||||
)
|
||||
|
||||
//If a spell creates a datum with vars it overrides, this list should contain an association with the variable containing the path of the created datum.
|
||||
var/static/list/special_list_vars = list(
|
||||
"projectile_var_overrides" = "projectile_type",
|
||||
"hand_var_overrides" = "hand_path",
|
||||
)
|
||||
|
||||
var/static/list/special_var_lists = list(
|
||||
"projectile_type" = "projectile_var_overrides",
|
||||
"hand_path" = "hand_var_overrides",
|
||||
)
|
||||
|
||||
var/static/list/enum_vars = list(
|
||||
"invocation_type" = list("none", "whisper", "shout", "emote"),
|
||||
"selection_type" = list("view", "range"),
|
||||
"smoke_spread" = list(0, 1, 2, 3),
|
||||
"random_target_priority" = list(0, 1),
|
||||
)
|
||||
|
||||
//base64 representations of any icons that may need to be displayed
|
||||
var/action_icon_base64
|
||||
var/projectile_icon_base64
|
||||
var/hand_icon_base64
|
||||
var/overlay_icon_base64
|
||||
var/mouse_icon_base64
|
||||
|
||||
/datum/give_sdql_spell/New(user, target)
|
||||
if(!CONFIG_GET(flag/sdql_spells))
|
||||
to_chat(user, span_warning("SDQL spells are disabled."))
|
||||
qdel(src)
|
||||
return
|
||||
src.user = CLIENT_FROM_VAR(user)
|
||||
|
||||
if(istype(target, /obj/effect/proc_holder/spell))
|
||||
target_spell = target
|
||||
var/mob/living/spell_owner = target_spell.owner.resolve()
|
||||
if(spell_owner)
|
||||
target_mob = spell_owner
|
||||
else
|
||||
to_chat(user, span_warning("[target_spell] does not have an owner, or its owner was qdelled. This REALLY shouldn't happen."))
|
||||
qdel(src)
|
||||
return
|
||||
else if(isliving(target))
|
||||
target_mob = target
|
||||
else
|
||||
to_chat(user, span_warning("Invalid target."))
|
||||
qdel(src)
|
||||
return
|
||||
if(target_spell)
|
||||
load_vars_from(target_spell)
|
||||
|
||||
/datum/give_sdql_spell/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "SDQLSpellMenu", "Give SDQL Spell")
|
||||
ui.open()
|
||||
ui.set_autoupdate(FALSE)
|
||||
|
||||
/datum/give_sdql_spell/ui_state(mob/user)
|
||||
return GLOB.admin_state
|
||||
|
||||
/datum/give_sdql_spell/ui_status(mob/user, datum/ui_state/state)
|
||||
if(QDELETED(target_mob))
|
||||
return UI_CLOSE
|
||||
return ..()
|
||||
|
||||
/datum/give_sdql_spell/ui_close(mob/user)
|
||||
qdel(src)
|
||||
|
||||
#define SANITIZE_NULLIFY 0
|
||||
#define SANITIZE_STRINGIFY 1
|
||||
|
||||
/datum/give_sdql_spell/ui_data(mob/user, params)
|
||||
var/list/data = list()
|
||||
if(target_spell)
|
||||
data["type"] = copytext("[target_spell.type]", 31, -5)
|
||||
data["fixed_type"] = TRUE
|
||||
else
|
||||
data["type"] = spell_type
|
||||
data["fixed_type"] = FALSE
|
||||
data["saved_vars"] = saved_vars
|
||||
data["list_vars"] = json_sanitize_list_vars(list_vars, SANITIZE_STRINGIFY)
|
||||
if(parse_result)
|
||||
data["parse_errors"] = parse_result["parse_errors"]
|
||||
data["parsed_type"] = parse_result["type"]
|
||||
data["parsed_vars"] = parse_result["vars"]
|
||||
data["parsed_list_vars"] = json_sanitize_list_vars(parse_result["list_vars"], SANITIZE_STRINGIFY)
|
||||
else
|
||||
data["parse_errors"] = null
|
||||
data["parsed_type"] = null
|
||||
data["parsed_vars"] = null
|
||||
data["parsed_list_vars"] = null
|
||||
data["action_icon"] = action_icon_base64
|
||||
data["projectile_icon"] = projectile_icon_base64
|
||||
data["hand_icon"] = hand_icon_base64
|
||||
data["overlay_icon"] = overlay_icon_base64
|
||||
data["mouse_icon"] = mouse_icon_base64
|
||||
data["alert"] = alert
|
||||
alert = ""
|
||||
return data
|
||||
|
||||
/datum/give_sdql_spell/ui_static_data(mob/user)
|
||||
return list(
|
||||
"types" = list("aimed", "aoe_turf", "cone", "cone/staggered", "pointed", "self", "targeted", "targeted/touch"),
|
||||
"tooltips" = list(
|
||||
"query" = "The SDQL query that is executed. Certain keywords are specific to SDQL spell queries.\n\
|
||||
$type\n\
|
||||
USER is replaced with a reference to the user of the spell.\n\
|
||||
TARGETS_AND_USER is replaced with the combined references from TARGETS and USER.\n\
|
||||
SOURCE is replaced with a reference to this spell, allowing you to refer to and edit variables within it.\n\
|
||||
SCRATCHPAD is a list used to store variables between individual queries within the same cast or between multiple casts.\n\
|
||||
NOTE: The SDQL keywords usr and marked do not work.",
|
||||
"query_aimed" = "TARGETS is replaced with a list containing a reference to the atom hit by the fired projectile.",
|
||||
"query_aoe_turf" = "TARGETS is replaced with a list containing references to every atom in the spell's area of effect.",
|
||||
"query_cone" = "TARGETS is replaced with a list containing references to every atom in the cone produced by the spell.",
|
||||
"query_cone/staggered" = "The query will be executed once for every level of the cone produced by the spell.\n\
|
||||
TARGETS is replaced with a list containing references to every atom in the given level of the cone.",
|
||||
"query_pointed" = "TARGETS is replaced with a list containing a reference to the targeted atom.",
|
||||
"query_self" = "TARGETS is null.",
|
||||
"query_targeted" = "TARGETS is replaced with a list containing a reference(s) to the targeted mob(s).",
|
||||
"query_targeted_touch" = "TARGETS is replaced with a list containing a reference to the atom hit with the touch attack.",
|
||||
"charge_type" = "How the spell's charge works. This affects how charge_max is used.\n\
|
||||
When set to \"recharge\", charge_max is the time in deciseconds between casts of the spell.\n\
|
||||
When set to \"charges\", the user can only use the spell a number of times equal to charge_max.\n\
|
||||
When set to \"holder_var\", charge_max is not used. holder_var_type and holder_var_amount are used instead.\n",
|
||||
"holder_var_type" = "When charge_type is set to \"holder_var\", this is the name of the var that is modified each time the spell is cast.\n\
|
||||
If this is set to \"bruteloss\", \"fireloss\", \"toxloss\", or \"oxyloss\", the user will take the corresponding damage.\n\
|
||||
If this is set to \"stun\", \"knockdown\", \"paralyze\", \"immobilize\", or \"unconscious\", the user will suffer the corresponding status effect.\n\
|
||||
If this is set to anything else, the variable with the appropriate name will be modified.",
|
||||
"holder_var_amount" = "The amount of damage taken, the duration of status effect inflicted, or the change made to any other variable.",
|
||||
"clothes_req" = "Whether the user has to be wearing wizard robes to cast the spell.",
|
||||
"cult_req" = "Whether the user has to be wearing cult robes to cast the spell.",
|
||||
"human_req" = "Whether the user has to be a human to cast the spell. Redundant when clothes_req is true.",
|
||||
"nonabstract_req" = "If this is true, the spell cannot be cast by brains and pAIs.",
|
||||
"stat_allowed" = "Whether the spell can be cast if the user is unconscious or dead.",
|
||||
"phase_allowed" = "Whether the spell can be cast while the user is jaunting or bloodcrawling.",
|
||||
"antimagic_allowed" = "Whether the spell can be cast while the user is affected by anti-magic effects.",
|
||||
"invocation_type" = "How the spell is invoked.\n\
|
||||
When set to \"whisper\", the user whispers the invocation, as if with the whisper verb.\n\
|
||||
When set to \"shout\", the user says the invocation, as if with the say verb.\n\
|
||||
When set to \"emote\", a visible message is produced.",
|
||||
"invocation" = "What the user says, whispers, or emotes when using the spell.",
|
||||
"invocation_emote_self" = "What the user sees in their own chat when they use the spell.",
|
||||
"selection_type" = "Whether the spell can target any mob in range, or only visible mobs in range.",
|
||||
"range" = "The spell's range, in tiles.",
|
||||
"message" = "What mobs affected by the spell see in their chat.\n\
|
||||
Keep in mind, just because a mob is affected by the spell doesn't mean the query will have any effect on them.",
|
||||
"player_lock" = "If false, simple mobs can use the spell.",
|
||||
"overlay" = "Whether an overlay is drawn atop atoms affectecd by the spell.\n\
|
||||
Keep in mind, just because an atom is affected by the spell doesn't mean the query will have any effect on it.",
|
||||
"overlay_lifetime" = "The amount of time in deciseconds the overlay will persist.",
|
||||
"sparks_spread" = "Whether the spell produces sparks when cast.",
|
||||
"smoke_spread" = "The kind of smoke, if any, the spell produces when cast.",
|
||||
"centcom_cancast" = "If true, the spell can be cast on the centcom Z-level.",
|
||||
"max_targets" = "The maximum number of mobs the spell can target.",
|
||||
"target_ignore_prev" = "If false, the same mob can be targeted multiple times.",
|
||||
"include_user" = "If true, the user can target themselves with the spell.",
|
||||
"random_target" = "If true, the spell will target a random mob(s) in range.",
|
||||
"random_target_priority" = "Whether the spell will target random mobs in range or the closest mobs in range.",
|
||||
"inner_radius" = "If this is a non-negative number, the spell will not affect atoms within that many tiles of the user.",
|
||||
"ranged_mousepointer" = "The icon used for the mouse when aiming the spell.",
|
||||
"deactive_mesg" = "The message the user sees when canceling the spell.",
|
||||
"active_msg" = "The message the user sees when activating the spell.",
|
||||
"projectile_amount" = "The maximum number of projectiles the user can fire with each cast of the spell.",
|
||||
"projectiles_per_fire" = "The amount of projectiles fired with each click of the mouse.",
|
||||
"projectile_var_overrides" = "The fired projectiles will have the appropriate variables overridden by the corresponding values in this associative list.\n\
|
||||
You should probably set \"name\", \"icon\", and \"icon_state\".\n\
|
||||
Refer to code/modules/projectiles/projectile.dm to see what other vars you can override.",
|
||||
"cone_level" = "How many tiles out the cone will extend.",
|
||||
"respect_density" = "If true, the cone produced by the spell is blocked by walls.",
|
||||
"self_castable" = "If true, the user can cast the spell on themselves.",
|
||||
"aim_assist" = "If true, the spell has turf-based aim assist.",
|
||||
"drawmessage" = "The message the user sees when activating the spell.",
|
||||
"dropmessage" = "The message the user sees when canceling the spell.",
|
||||
"hand_var_overrides" = "The touch attack will have the appropriate variables overridden by the corresponding values in this associative list.\n\
|
||||
You should probably set \"name\", \"desc\", \"catchphrase\", \"on_use_sound\" \"icon\", \"icon_state\", and \"inhand_icon_state\".\n\
|
||||
Refer to code/modules/spells/spell_types/godhand.dm to see what other vars you can override.",
|
||||
"scratchpad" = "This list can be used to store variables between individual queries within the same cast or between casts.\n\
|
||||
You can declare variables from this menu for convenience. To access this list in a query, use the identifier \"SOURCE.scratchpad\".\n\
|
||||
Refer to the _list procs defined in code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm for information on how to modify and edit list vars from within a query.",
|
||||
),
|
||||
)
|
||||
|
||||
#define LIST_VAR_FLAGS_TYPED 1
|
||||
#define LIST_VAR_FLAGS_NAMED 2
|
||||
|
||||
/datum/give_sdql_spell/proc/load_vars_from(obj/effect/proc_holder/spell/sample)
|
||||
var/datum/component/sdql_executor/executor = sample.GetComponent(/datum/component/sdql_executor)
|
||||
if(!executor)
|
||||
CRASH("[sample]'s SDQL executor component went missing!")
|
||||
saved_vars["query"] = executor.query
|
||||
load_list_var(executor.scratchpad, "scratchpad")
|
||||
for(var/V in sample.vars&editable_spell_vars)
|
||||
if(islist(sample.vars[V]))
|
||||
if(special_list_vars[V])
|
||||
var/list/saved_overrides = executor.saved_overrides[V]
|
||||
if(saved_overrides)
|
||||
list_vars[V] = saved_overrides.Copy()
|
||||
icon_needs_updating("[V]/icon")
|
||||
else
|
||||
saved_vars[V] = sample.vars[V]
|
||||
icon_needs_updating(V)
|
||||
|
||||
/datum/give_sdql_spell/proc/load_list_var(list/L, list_name)
|
||||
list_vars[list_name] = list()
|
||||
for(var/V in L)
|
||||
if(islist(L[V]))
|
||||
list_vars[list_name][V] = list("type" = "list", "value" = null)
|
||||
list_vars |= load_list_var(L[V], "[list_name]/[V]")
|
||||
else if(isnum(L[V]))
|
||||
list_vars[list_name][V] = list("type" = "num", "value" = L[V])
|
||||
else if(ispath(L[V]))
|
||||
list_vars[list_name][V] = list("type" = "path", "value" = L[V])
|
||||
else if(isicon(L[V]))
|
||||
list_vars[list_name][V] = list("type" = "icon", "value" = L[V])
|
||||
else if(istext(L[V]) || isfile(L[V]))
|
||||
list_vars[list_name][V] = list("type" = "string", "value" = L[V])
|
||||
else if(istype(L[V], /datum))
|
||||
list_vars[list_name][V] = list("type" = "ref", "value" = L[V])
|
||||
else if(isnull(L[V]))
|
||||
list_vars[list_name][V] = list("type" = "num", "value" = 0)
|
||||
alert = "Could not determine the type for [list_name]/[V]! Be sure to set it correctly, or you may cause unnecessary runtimes!"
|
||||
|
||||
/datum/give_sdql_spell/ui_act(action, params, datum/tgui/ui)
|
||||
if(..())
|
||||
return
|
||||
. = TRUE
|
||||
switch(action)
|
||||
if("type")
|
||||
if(!target_spell)
|
||||
spell_type = params["path"]
|
||||
load_sample()
|
||||
if("variable")
|
||||
var/V = params["name"]
|
||||
if(V == "holder_var_type")
|
||||
if(!holder_var_validate(params["value"]))
|
||||
return
|
||||
saved_vars[V] = params["value"]
|
||||
icon_needs_updating(V)
|
||||
if("bool_variable")
|
||||
saved_vars[params["name"]] = !saved_vars[params["name"]]
|
||||
if("path_variable")
|
||||
var/new_path = tgui_input_list(user, "Select type.", "Add SDQL Spell", typesof(text2path(params["root_path"])))
|
||||
if(new_path)
|
||||
saved_vars[params["name"]] = new_path
|
||||
var/datum/sample = new new_path
|
||||
var/list/overrides = list_vars[special_var_lists[params["name"]]]
|
||||
overrides = overrides&sample.vars
|
||||
qdel(sample)
|
||||
icon_needs_updating(params["name"])
|
||||
if("list_variable_add")
|
||||
if(!list_vars[params["list"]])
|
||||
list_vars[params["list"]] = list()
|
||||
if(special_list_vars[params["list"]])
|
||||
var/path = saved_vars[special_list_vars[params["list"]]]
|
||||
var/datum/sample = new path
|
||||
var/list/choosable_vars = map_var_list(sample.vars-list_vars[params["list"]], sample)
|
||||
var/chosen_var = tgui_input_list(user, "Select variable to add.", "Add SDQL Spell", sortList(choosable_vars))
|
||||
if(chosen_var)
|
||||
if(islist(sample.vars[choosable_vars[chosen_var]]))
|
||||
list_vars[params["list"]][choosable_vars[chosen_var]] = list("type" = "list", "value" = null, "flags" = LIST_VAR_FLAGS_TYPED|LIST_VAR_FLAGS_NAMED)
|
||||
list_vars["[params["list"]]/[choosable_vars[chosen_var]]"] = list()
|
||||
else if(isnum(sample.vars[choosable_vars[chosen_var]]))
|
||||
list_vars[params["list"]][choosable_vars[chosen_var]] = list("type" = "num", "value" = sample.vars[choosable_vars[chosen_var]], "flags" = LIST_VAR_FLAGS_TYPED|LIST_VAR_FLAGS_NAMED)
|
||||
else if(ispath(sample.vars[choosable_vars[chosen_var]]))
|
||||
list_vars[params["list"]][choosable_vars[chosen_var]] = list("type" = "path", "value" = sample.vars[choosable_vars[chosen_var]], "flags" = LIST_VAR_FLAGS_TYPED|LIST_VAR_FLAGS_NAMED)
|
||||
else if(isicon(sample.vars[choosable_vars[chosen_var]]))
|
||||
list_vars[params["list"]][choosable_vars[chosen_var]] = list("type" = "icon", "value" = sample.vars[choosable_vars[chosen_var]], "flags" = LIST_VAR_FLAGS_TYPED|LIST_VAR_FLAGS_NAMED)
|
||||
else if(istext(sample.vars[choosable_vars[chosen_var]]) || isfile(sample.vars[choosable_vars[chosen_var]]))
|
||||
list_vars[params["list"]][choosable_vars[chosen_var]] = list("type" = "string", "value" = sample.vars[choosable_vars[chosen_var]], "flags" = LIST_VAR_FLAGS_TYPED|LIST_VAR_FLAGS_NAMED)
|
||||
else if(istype(sample.vars[choosable_vars[chosen_var]], /datum))
|
||||
list_vars[params["list"]][choosable_vars[chosen_var]] = list("type" = "ref", "value" = null, "flags" = LIST_VAR_FLAGS_TYPED|LIST_VAR_FLAGS_NAMED)
|
||||
alert = "[params["list"]]/[choosable_vars[chosen_var]] is a reference! Be sure to set it correctly, or you may cause unnecessary runtimes!"
|
||||
else if(isnull(sample.vars[choosable_vars[chosen_var]]))
|
||||
list_vars[params["list"]][choosable_vars[chosen_var]] = list("type" = "num", "value" = 0, "flags" = LIST_VAR_FLAGS_NAMED)
|
||||
alert = "Could not determine the type for [params["list"]]/[choosable_vars[chosen_var]]! Be sure to set it correctly, or you may cause unnecessary runtimes!"
|
||||
else
|
||||
alert = "[params["list"]]/[choosable_vars[chosen_var]] is not of a supported type!"
|
||||
icon_needs_updating("[params["list"]]/[choosable_vars[chosen_var]]")
|
||||
qdel(sample)
|
||||
else
|
||||
if(!list_vars[params["list"]]["new_var"])
|
||||
list_vars[params["list"]] += list("new_var" = list("type" = "num", "value" = 0, "flags" = 0))
|
||||
else
|
||||
alert = "Rename or remove [params["list"]]/new_var before attempting to add another variable to this list!"
|
||||
if("list_variable_remove")
|
||||
remove_list_var(params["list"], params["name"])
|
||||
if("list_variable_rename")
|
||||
rename_list_var(params["list"], params["name"], params["new_name"])
|
||||
if("list_variable_change_type")
|
||||
change_list_var_type(params["list"], params["name"], params["value"])
|
||||
if("list_variable_change_value")
|
||||
set_list_var(params["list"], params["name"], params["value"])
|
||||
icon_needs_updating("[params["list"]]/[params["name"]]")
|
||||
if("list_variable_change_bool")
|
||||
toggle_list_var(params["list"], params["name"])
|
||||
if("list_variable_set_ref")
|
||||
set_list_ref_var(params["list"], params["name"])
|
||||
if("save")
|
||||
var/f = file("data/TempSpellUpload")
|
||||
fdel(f)
|
||||
WRITE_FILE(f, json_encode(list("type" = spell_type, "vars" = saved_vars, "list_vars" = json_sanitize_list_vars(list_vars))))
|
||||
user << ftp(f,"[replacetext_char(saved_vars["name"], " ", "_")].json")
|
||||
if("load")
|
||||
var/spell_file = input("Pick spell json file:", "File") as null|file
|
||||
if(!spell_file)
|
||||
return
|
||||
var/filedata = file2text(spell_file)
|
||||
var/json = json_decode(filedata)
|
||||
if(!json)
|
||||
alert = "JSON decode error!"
|
||||
return
|
||||
parse_result = load_from_json(json)
|
||||
var/list/parse_errors = parse_result["parse_errors"]
|
||||
if(!parse_errors.len)
|
||||
finalize_load()
|
||||
if("close_error")
|
||||
parse_result = null
|
||||
if("load_despite_error")
|
||||
finalize_load()
|
||||
if("confirm")
|
||||
if(target_spell)
|
||||
reassign_vars(target_spell)
|
||||
target_spell.action.UpdateButtonIcon()
|
||||
log_admin("[key_name(user)] edited the SDQL spell \"[target_spell]\" owned by [key_name(target_mob)].")
|
||||
else
|
||||
var/new_spell = give_spell()
|
||||
log_admin("[key_name(user)] gave the SDQL spell \"[new_spell]\" to [key_name(target_mob)].")
|
||||
ui.close()
|
||||
|
||||
/datum/give_sdql_spell/proc/load_sample()
|
||||
var/path = text2path("/obj/effect/proc_holder/spell/[spell_type]/sdql")
|
||||
var/datum/sample = new path
|
||||
if(spell_type)
|
||||
load_vars_from(sample)
|
||||
qdel(sample)
|
||||
|
||||
/datum/give_sdql_spell/proc/finalize_load()
|
||||
spell_type = parse_result["type"]
|
||||
load_sample()
|
||||
saved_vars = parse_result["vars"] | saved_vars
|
||||
list_vars = parse_result["list_vars"] | list_vars
|
||||
parse_result = null
|
||||
icon_needs_updating("everything")
|
||||
|
||||
//Change all references in the list vars, either to null (for saving) or to their string representation (for display)
|
||||
/datum/give_sdql_spell/proc/json_sanitize_list_vars(list/list_vars, mode = SANITIZE_NULLIFY)
|
||||
var/list/temp_list_vars = deepCopyList(list_vars)
|
||||
for(var/V in temp_list_vars)
|
||||
var/list/L = temp_list_vars[V]
|
||||
for(var/W in L)
|
||||
if(temp_list_vars[V][W]["type"] == "ref")
|
||||
switch(mode)
|
||||
if(SANITIZE_NULLIFY)
|
||||
temp_list_vars[V][W]["value"] = null
|
||||
if(SANITIZE_STRINGIFY)
|
||||
if(temp_list_vars[V][W]["value"])
|
||||
temp_list_vars[V][W]["value"] = "[temp_list_vars[V][W]["value"]]"
|
||||
else
|
||||
temp_list_vars[V][W]["value"] = "null"
|
||||
return temp_list_vars
|
||||
|
||||
#undef SANITIZE_NULLIFY
|
||||
#undef SANITIZE_STRINGIFY
|
||||
|
||||
/datum/give_sdql_spell/proc/load_from_json(json)
|
||||
var/list/result_vars = list()
|
||||
var/list/result_list_vars = list()
|
||||
var/list/parse_errors = list()
|
||||
. = list("type" = "",
|
||||
"vars" = result_vars,
|
||||
"list_vars" = result_list_vars,
|
||||
"parse_errors" = parse_errors)
|
||||
if(!json["type"])
|
||||
parse_errors += "The \"type\" property is missing from the json file"
|
||||
return
|
||||
var/temp_type = json["type"]
|
||||
var/datum/D = text2path("/obj/effect/proc_holder/spell/[temp_type]/sdql")
|
||||
if(!ispath(D))
|
||||
parse_errors += "[temp_type] is not a valid SDQL spell type"
|
||||
return
|
||||
if(target_spell)
|
||||
if(!istype(target_spell, D))
|
||||
parse_errors += "You cannot change the type of an existing spell"
|
||||
if(!json["vars"])
|
||||
parse_errors += "The \"vars\" property is missing from the json file"
|
||||
if(!islist(json["vars"]))
|
||||
parse_errors += "The \"vars\" property must be a json object"
|
||||
if(!json["list_vars"])
|
||||
parse_errors += "The \"list_vars\" property is missing from the json file"
|
||||
if(!islist(json["list_vars"]))
|
||||
parse_errors += "The \"list_vars\" property must be a json object"
|
||||
if(parse_errors.len)
|
||||
return
|
||||
.["type"] = temp_type
|
||||
var/list/temp_vars = json["vars"]
|
||||
var/list/temp_list_vars = json["list_vars"]
|
||||
D = new D
|
||||
for(var/V in temp_vars)
|
||||
if(!istext(V))
|
||||
parse_errors += "JSON property names must be text ([V] is not text)"
|
||||
continue
|
||||
if(V == "query")
|
||||
if(!istext(temp_vars[V]))
|
||||
parse_errors += "The value of \"query\" must be text"
|
||||
continue
|
||||
result_vars[V] = temp_vars[V]
|
||||
continue
|
||||
if(!(V in editable_spell_vars))
|
||||
parse_errors += "\"[V]\" is not an editable variable"
|
||||
continue
|
||||
if(!(V in D.vars)) //D.vars[V] can runtime unlike V in D.vars
|
||||
parse_errors += "Spells of type \"[temp_type]\" have no such var [V]"
|
||||
continue
|
||||
if(islist(D.vars[V]))
|
||||
parse_errors += "[D.type]/[V] is a list; vars.[V] should be in the \"list_vars\" property"
|
||||
continue
|
||||
if(istext(D.vars[V]))
|
||||
if(!istext(temp_vars[V]))
|
||||
parse_errors += "[D.type]/[V] is text; vars.[V] has been converted to text"
|
||||
temp_vars[V] = "[temp_vars[V]]"
|
||||
continue
|
||||
if(V=="holder_var_type")
|
||||
var/potential_alert = holder_var_validate(temp_vars[V], TRUE)
|
||||
if(potential_alert)
|
||||
parse_errors += potential_alert
|
||||
continue
|
||||
if(isicon(D.vars[V]))
|
||||
if(!istext(temp_vars[V]))
|
||||
parse_errors += "[D.type]/[V] is an icon; vars.[V] has been converted to text"
|
||||
temp_vars[V] = "[temp_vars[V]]"
|
||||
if(!fexists(temp_vars[V]))
|
||||
parse_errors += "[D.type]/[V] is an icon; no such file [temp_vars[V]] exists on the server"
|
||||
continue
|
||||
if(ispath(D.vars[V]))
|
||||
if(!istext(temp_vars[V]))
|
||||
parse_errors += "[D.type]/[V] is a path; vars.[V] has been converted to text"
|
||||
temp_vars[V] = "[temp_vars[V]]"
|
||||
var/path = text2path(temp_vars[V])
|
||||
if(!path)
|
||||
parse_errors += "[D.type]/[V] is a path; vars.[V] ([temp_vars[V]]) does not correspond to an existing type"
|
||||
continue
|
||||
if(!ispath(path, D.vars[V]))
|
||||
parse_errors += "[D.type]/[V] is a path; vars.[V] ([path]) is not derived from [D.vars[V]]"
|
||||
continue
|
||||
if(isnum(D.vars[V]))
|
||||
if(!isnum(temp_vars[V]))
|
||||
parse_errors += "[D.type]/[V] is a number; vars.[V] should be a number"
|
||||
continue
|
||||
if(enum_vars[V])
|
||||
var/list/enum = enum_vars[V]
|
||||
if(!enum.Find(temp_vars[V]))
|
||||
parse_errors += "[D.type]/[V] is an enumeration; vars.[V] should be one of: [english_list(enum, and_text = " or ")]"
|
||||
continue
|
||||
result_vars[V] = temp_vars[V]
|
||||
for(var/V in temp_list_vars)
|
||||
if(!istext(V))
|
||||
parse_errors += "JSON property names must be text ([V] is not text)"
|
||||
continue
|
||||
if(!islist(temp_list_vars[V]))
|
||||
parse_errors += "list_vars.[V] should be a json object"
|
||||
continue
|
||||
if(special_list_vars[V] && (V in D.vars))
|
||||
var/sample_path = D.vars[special_list_vars[V]]
|
||||
var/temp_path
|
||||
if(temp_vars[special_list_vars[V]])
|
||||
temp_path = temp_vars[special_list_vars[V]]
|
||||
temp_path = text2path(temp_path)
|
||||
if(!temp_path)
|
||||
parse_errors += "[D.type]/[special_list_vars[V]] is a path; vars.[temp_vars[special_list_vars[V]]] (temp_vars[special_list_vars[V]]) does not correspond to an existing type"
|
||||
else if(!ispath(temp_path, D.vars[special_list_vars[V]]))
|
||||
parse_errors += "[D.type]/[special_list_vars[V]] is a path; vars.[special_list_vars[V]] ([temp_path]) is not derived from [D.vars[special_list_vars[V]]]"
|
||||
else
|
||||
sample_path = temp_path
|
||||
result_list_vars[V] = list()
|
||||
var/datum/sample = new sample_path
|
||||
for(var/W in temp_list_vars[V])
|
||||
if(!istext(W))
|
||||
parse_errors += "JSON property names must be text ([W] in list_vars.[V] is not text)"
|
||||
continue
|
||||
if(!(W in sample.vars))
|
||||
parse_errors += "[sample.type] has no such var \"[W]\""
|
||||
continue
|
||||
if(!(islist(temp_list_vars[V][W]) && istext(temp_list_vars[V][W]["type"]) && (istext(temp_list_vars[V][W]["value"]) || isnum(temp_list_vars[V][W]["value"]) || isnull(temp_list_vars[V][W]["value"])) && isnum(temp_list_vars[V][W]["flags"])))
|
||||
parse_errors += "[V]/[W] is not of the form {type: string, value: num|string|null, flags: number}"
|
||||
continue
|
||||
if(!(temp_list_vars[V][W]["flags"] & LIST_VAR_FLAGS_NAMED))
|
||||
parse_errors += "[V]/[W] did not have the LIST_VAR_FLAGS_NAMED flag set; it has been set"
|
||||
temp_list_vars[V][W]["flags"] |= LIST_VAR_FLAGS_NAMED
|
||||
if(temp_list_vars & ~(LIST_VAR_FLAGS_NAMED | LIST_VAR_FLAGS_NAMED))
|
||||
parse_errors += "[V]/[W] has unused bit flags set; they have been unset"
|
||||
temp_list_vars[V][W]["flags"] &= LIST_VAR_FLAGS_NAMED | LIST_VAR_FLAGS_NAMED
|
||||
if(!(temp_list_vars[V][W]["flags"] & LIST_VAR_FLAGS_TYPED))
|
||||
if(isnull(sample.vars[W]))
|
||||
continue
|
||||
parse_errors += "[sample.type]/[W] is not null; it has had the LIST_VAR_FLAGS_TYPED flag set"
|
||||
temp_list_vars[V][W]["flags"] |= LIST_VAR_FLAGS_TYPED
|
||||
if(islist(sample.vars[W]))
|
||||
temp_list_vars[V][W]["type"] = "list"
|
||||
else if(isnum(sample.vars[W]))
|
||||
temp_list_vars[V][W]["type"] = "num"
|
||||
else if(istext(sample.vars[W]))
|
||||
temp_list_vars[V][W]["type"] = "string"
|
||||
else if(ispath(sample.vars[W]))
|
||||
temp_list_vars[V][W]["type"] = "path"
|
||||
else if(isicon(sample.vars[W]))
|
||||
temp_list_vars[V][W]["type"] = "icon"
|
||||
else if(istype(sample.vars[W], /datum))
|
||||
temp_list_vars[V][W]["type"] = "ref"
|
||||
temp_list_vars[V][W]["value"] = null
|
||||
else
|
||||
parse_errors += "[sample.type]/[W] is not of a supported type"
|
||||
continue
|
||||
if(islist(sample.vars[W]))
|
||||
if(temp_list_vars[V][W]["type"] != "list")
|
||||
parse_errors += "[sample.type]/[W] is a list; list_vars.[V].[W].type has been converted to \"list\""
|
||||
temp_list_vars[V][W]["type"] = "list"
|
||||
if(!istext(temp_list_vars[V][W]["value"]))
|
||||
parse_errors += "list_vars.[V].[W].type is \"list\"; list_vars.[V].[W].value ([temp_list_vars[V][W]["value"]]) has been converted to text"
|
||||
temp_list_vars[V][W]["value"] = "[temp_list_vars[V][W]["value"]]"
|
||||
if(!temp_list_vars[temp_list_vars[V][W]["value"]])
|
||||
parse_errors += "list_vars.[V].[W].type is \"list\"; there is no property of list_vars whose name is list_vars.[V].[W].value ([temp_list_vars[V][W]["value"]])"
|
||||
continue
|
||||
else if(isnum(sample.vars[W]))
|
||||
if(temp_list_vars[V][W]["type"] != "num")
|
||||
parse_errors += "[sample.type]/[W] is a number; list_vars.[V].[W].type has been converted to \"num\""
|
||||
temp_list_vars[V][W]["type"] = "num"
|
||||
if(!isnum(temp_list_vars[V][W]["value"]))
|
||||
parse_errors += "list_vars.[V].[W].type is \"num\"; list_vars.[V].[W].value ([temp_list_vars[V][W]["value"]]) should be a number"
|
||||
continue
|
||||
else if(istext(sample.vars[W]))
|
||||
if(temp_list_vars[V][W]["type"] != "string")
|
||||
parse_errors += "[sample.type]/[W] is text; list_vars.[V].[W].type has been converted to \"string\""
|
||||
temp_list_vars[V][W]["type"] = "string"
|
||||
if(!istext(temp_list_vars[V][W]["value"]))
|
||||
parse_errors += "list_vars.[V].[W].type is \"list\"; list_vars.[V].[W].value ([temp_list_vars[V][W]["value"]]) has been converted to text"
|
||||
temp_list_vars[V][W]["value"] = "[temp_list_vars[V][W]["value"]]"
|
||||
else if(ispath(sample.vars[W]))
|
||||
if(temp_list_vars[V][W]["type"] != "path")
|
||||
parse_errors += "[sample.type]/[W] is a path; list_vars.[V].[W].type has been converted to \"path\""
|
||||
temp_list_vars[V][W]["type"] = "path"
|
||||
if(!istext(temp_list_vars[V][W]["value"]))
|
||||
parse_errors += "list_vars.[V].[W].type is \"path\"; list_vars.[V].[W].value ([temp_list_vars[V][W]["value"]]) has been converted to text"
|
||||
temp_list_vars[V][W]["value"] = "[temp_list_vars[V][W]["value"]]"
|
||||
temp_path = text2path(temp_list_vars[V][W]["value"])
|
||||
if(!ispath(temp_path))
|
||||
parse_errors += "list_vars.[W].[W].type is \"path\"; list_vars.[V].[W].value ([temp_list_vars[V][W]["value"]]) does not correspond to an existing type"
|
||||
continue
|
||||
if(!ispath(temp_path, sample.vars[W]))
|
||||
parse_errors += "list_vars.[W].[W].type is \"path\"; list_vars.[V].[W].value ([temp_list_vars[V][W]["value"]]) is not derived from [sample.vars[W]]"
|
||||
continue
|
||||
else if(isicon(sample.vars[W]))
|
||||
if(temp_list_vars[V][W]["type"] != "icon")
|
||||
parse_errors += "[sample.type]/[W] is an icon; list_vars.[V].[W].type has been converted to \"icon\""
|
||||
temp_list_vars[V][W]["type"] = "icon"
|
||||
if(!istext(temp_list_vars[V][W]["value"]))
|
||||
parse_errors += "list_vars.[V].[W].type is \"icon\"; list_vars.[V].[W].value ([temp_list_vars[V][W]["value"]]) has been converted to text"
|
||||
temp_list_vars[V][W]["value"] = "[temp_list_vars[V][W]["value"]]"
|
||||
if(!fexists(temp_list_vars[V][W]["value"]))
|
||||
parse_errors += "list_vars.[V].[W].type is \"icon\"; no such file \"[temp_list_vars[V][W]["value"]]\" exists on the server"
|
||||
continue
|
||||
else if(istype(sample.vars[W], /datum))
|
||||
if(temp_list_vars[V][W]["type"] != "ref")
|
||||
parse_errors += "[sample.type]/[W] is a datum reference; list_vars.[V].[W].type has been converted to \"ref\""
|
||||
alert = "Reference vars are not assigned on load from file. Be sure to set them correctly."
|
||||
temp_list_vars[V][W]["type"] = "ref"
|
||||
temp_list_vars[V][W]["value"] = null
|
||||
result_list_vars[V][W] = temp_list_vars[V][W]
|
||||
qdel(sample)
|
||||
else
|
||||
result_list_vars[V] = list()
|
||||
for(var/W in temp_list_vars[V])
|
||||
if(temp_list_vars[V][W]["flags"])
|
||||
parse_errors += "list_vars.[V].[W] has unnecessary flags set; they have been unset"
|
||||
temp_list_vars[V][W]["flags"] = 0
|
||||
if(temp_list_vars[V][W]["type"] == "list")
|
||||
if(temp_list_vars[V][W]["value"])
|
||||
parse_errors += "list_vars.[V].[W].type is \"list\"; list_vars.[V].[W].value ([temp_list_vars[V][W]["value"]]) has been nulled, as it is not used"
|
||||
temp_list_vars[V][W]["value"] = null
|
||||
if(!temp_list_vars["[V]/[W]"])
|
||||
parse_errors += "list_vars.[V].[W].type is \"list\"; there is no property of list_vars whose name is \"[V]/[W]\""
|
||||
continue
|
||||
if(temp_list_vars[V][W]["type"] == "num")
|
||||
if(!isnum(temp_list_vars[V][W]["value"]))
|
||||
parse_errors += "list_vars.[V].[W].type is \"num\"; list_vars.[V].[W].value ([temp_list_vars[V][W]["value"]]) should be a number"
|
||||
continue
|
||||
if(temp_list_vars[V][W]["type"] == "string")
|
||||
if(!istext(temp_list_vars[V][W]["value"]))
|
||||
parse_errors += "list_vars.[V].[W].type is \"string\"; list_vars.[V].[W].value ([temp_list_vars[V][W]["value"]]) has been converted to text"
|
||||
temp_list_vars[V][W]["value"] = "[temp_list_vars[V][W]["value"]]"
|
||||
if(temp_list_vars[V][W]["type"] == "path")
|
||||
if(!istext(temp_list_vars[V][W]["value"]))
|
||||
parse_errors += "list_vars.[V].[W].type is \"path\"; list_vars.[V].[W].value ([temp_list_vars[V][W]["value"]]) has been converted to text"
|
||||
temp_list_vars[V][W]["value"] = "[temp_list_vars[V][W]["value"]]"
|
||||
var/temp_path = text2path(temp_list_vars[V][W]["value"])
|
||||
if(!ispath(temp_path))
|
||||
parse_errors += "list_vars.[W].[W].type is \"path\"; list_vars.[V].[W].value ([temp_list_vars[V][W]["value"]]) does not correspond to an existing type"
|
||||
continue
|
||||
if(temp_list_vars[V][W]["type"] == "icon")
|
||||
if(!istext(temp_list_vars[V][W]["value"]))
|
||||
parse_errors += "list_vars.[V].[W].type is \"icon\"; list_vars.[V].[W].value ([temp_list_vars[V][W]["value"]]) has been converted to text"
|
||||
temp_list_vars[V][W]["value"] = "[temp_list_vars[V][W]["value"]]"
|
||||
if(!fexists(temp_list_vars[V][W]["value"]))
|
||||
parse_errors += "list_vars.[V].[W].type is \"icon\"; no such file \"[temp_list_vars[V][W]["value"]]\" exists on the server"
|
||||
continue
|
||||
if(temp_list_vars[V][W]["type"] == "ref")
|
||||
if(!isnull(temp_list_vars[V][W]["value"]))
|
||||
parse_errors += "list_vars.[V].[W].type is \"ref\"; list_vars.[V].[W].value has been nulled out"
|
||||
temp_list_vars[V][W]["value"] = null
|
||||
else
|
||||
alert = "Reference vars are not assigned on load from file. Be sure to set them correctly."
|
||||
result_list_vars[V][W] = temp_list_vars[V][W]
|
||||
qdel(D)
|
||||
|
||||
#undef LIST_VAR_FLAGS_TYPED
|
||||
#undef LIST_VAR_FLAGS_NAMED
|
||||
|
||||
/datum/give_sdql_spell/proc/map_var_list(list/L, datum/D)
|
||||
var/list/ret = list()
|
||||
for(var/V in L)
|
||||
if(D.vars[V])
|
||||
ret["[V] = [string_rep(D.vars[V])]"] = V
|
||||
return ret
|
||||
|
||||
/datum/give_sdql_spell/proc/string_rep(V)
|
||||
if(istext(V) || isfile(V) || isicon(V))
|
||||
return "\"[V]\""
|
||||
else if(isnull(V))
|
||||
return "null"
|
||||
else
|
||||
return "[V]"
|
||||
|
||||
/datum/give_sdql_spell/proc/holder_var_validate(V, return_alert = FALSE)
|
||||
switch(V)
|
||||
if("bruteloss", "fireloss", "toxloss", "oxyloss", "stun", "knockdown", "paralyze", "unconscious")
|
||||
if(return_alert)
|
||||
return ""
|
||||
return TRUE
|
||||
else
|
||||
if(target_mob.vars[V])
|
||||
if(!isnum(target_mob.vars[V]))
|
||||
var/new_alert = "[target_mob.type]/[V] is not a number!"
|
||||
if(return_alert)
|
||||
return new_alert
|
||||
alert = new_alert
|
||||
return FALSE
|
||||
else
|
||||
return return_alert ? "" : TRUE
|
||||
else
|
||||
var/new_alert = "[target_mob.type] has no such variable [V]!"
|
||||
if(return_alert)
|
||||
return new_alert
|
||||
alert = new_alert
|
||||
return FALSE
|
||||
|
||||
/datum/give_sdql_spell/proc/icon_needs_updating(var_name)
|
||||
switch(var_name)
|
||||
if("action_icon", "action_icon_state", "action_background_icon_state")
|
||||
var/icon/out_icon = icon('icons/effects/effects.dmi', "nothing")
|
||||
var/image/out_image = image('icons/mob/actions/backgrounds.dmi', null, saved_vars["action_background_icon_state"])
|
||||
var/overlay_icon = icon(saved_vars["action_icon"], saved_vars["action_icon_state"])
|
||||
out_image.overlays += image(overlay_icon)
|
||||
out_icon.Insert(getFlatIcon(out_image, no_anim = TRUE))
|
||||
action_icon_base64 = icon2base64(out_icon)
|
||||
|
||||
if("projectile_var_overrides/icon", "projectile_var_overrides/icon_state")
|
||||
var/atom/A = /obj/projectile
|
||||
var/icon = initial(A.icon)
|
||||
var/icon_state = initial(A.icon_state)
|
||||
if(list_vars["projectile_var_overrides"]?["icon"])
|
||||
icon = list_vars["projectile_var_overrides"]["icon"]["value"]
|
||||
if(list_vars["projectile_var_overrides"]?["icon_state"])
|
||||
icon_state = list_vars["projectile_var_overrides"]["icon_state"]["value"]
|
||||
var/icon/out_icon = icon(icon, icon_state, frame = 1)
|
||||
projectile_icon_base64 = icon2base64(out_icon)
|
||||
|
||||
if("hand_var_overrides/icon", "hand_var_overrides/icon_state")
|
||||
var/atom/A = /obj/item/melee/touch_attack
|
||||
var/icon = initial(A.icon)
|
||||
var/icon_state = initial(A.icon_state)
|
||||
if(list_vars["hand_var_overrides"]?["icon"])
|
||||
icon = list_vars["hand_var_overrides"]["icon"]["value"]
|
||||
if(list_vars["hand_var_overrides"]?["icon_state"])
|
||||
icon_state = list_vars["hand_var_overrides"]["icon_state"]["value"]
|
||||
var/icon/out_icon = icon(icon, icon_state, frame = 1)
|
||||
hand_icon_base64 = icon2base64(out_icon)
|
||||
|
||||
if("overlay", "overlay_icon", "overlay_icon_state")
|
||||
var/icon/out_icon = icon(saved_vars["overlay_icon"], saved_vars["overlay_icon_state"], frame = 1)
|
||||
overlay_icon_base64 = icon2base64(out_icon)
|
||||
|
||||
if("ranged_mousepointer")
|
||||
var/icon/out_icon = icon(saved_vars["ranged_mousepointer"], frame = 1)
|
||||
mouse_icon_base64 = icon2base64(out_icon)
|
||||
|
||||
if("everything")
|
||||
var/icon/out_icon = icon('icons/effects/effects.dmi', "nothing")
|
||||
var/image/out_image = image('icons/mob/actions/backgrounds.dmi', null, saved_vars["action_background_icon_state"])
|
||||
var/overlay_icon = icon(saved_vars["action_icon"], saved_vars["action_icon_state"])
|
||||
out_image.overlays += image(overlay_icon)
|
||||
out_icon.Insert(getFlatIcon(out_image, no_anim = TRUE))
|
||||
action_icon_base64 = icon2base64(out_icon)
|
||||
if(list_vars["projectile_var_overrides"])
|
||||
var/atom/A = saved_vars["projectile_type"]
|
||||
var/icon = initial(A.icon)
|
||||
var/icon_state = initial(A.icon_state)
|
||||
if(list_vars["projectile_var_overrides"]?["icon"])
|
||||
icon = list_vars["projectile_var_overrides"]["icon"]["value"]
|
||||
if(list_vars["projectile_var_overrides"]?["icon_state"])
|
||||
icon_state = list_vars["projectile_var_overrides"]["icon_state"]["value"]
|
||||
out_icon = icon(icon, icon_state, frame = 1)
|
||||
projectile_icon_base64 = icon2base64(out_icon)
|
||||
if(list_vars["hand_var_overrides"])
|
||||
var/atom/A = saved_vars["hand_path"]
|
||||
var/icon = initial(A.icon)
|
||||
var/icon_state = initial(A.icon_state)
|
||||
if(list_vars["hand_var_overrides"]?["icon"])
|
||||
icon = list_vars["hand_var_overrides"]["icon"]["value"]
|
||||
if(list_vars["hand_var_overrides"]?["icon_state"])
|
||||
icon_state = list_vars["hand_var_overrides"]["icon_state"]["value"]
|
||||
out_icon = icon(icon, icon_state, frame = 1)
|
||||
hand_icon_base64 = icon2base64(out_icon)
|
||||
out_icon = icon(saved_vars["overlay_icon"], saved_vars["overlay_icon_state"], frame = 1)
|
||||
overlay_icon_base64 = icon2base64(out_icon)
|
||||
out_icon = icon(saved_vars["ranged_mousepointer"], frame = 1)
|
||||
mouse_icon_base64 = icon2base64(out_icon)
|
||||
|
||||
/datum/give_sdql_spell/proc/toggle_list_var(list_name, list_var)
|
||||
if(list_vars[list_name]?[list_var])
|
||||
list_vars[list_name][list_var]["value"] = !list_vars[list_name][list_var]["value"]
|
||||
|
||||
/datum/give_sdql_spell/proc/set_list_var(list_name, list_var, value)
|
||||
if(list_vars[list_name]?[list_var])
|
||||
list_vars[list_name][list_var]["value"] = value
|
||||
|
||||
/datum/give_sdql_spell/proc/set_list_ref_var(list_name, list_var)
|
||||
if(list_vars[list_name]?[list_var])
|
||||
list_vars[list_name][list_var]["value"] = user.holder?.marked_datum
|
||||
|
||||
/datum/give_sdql_spell/proc/rename_list_var(list_name, list_var, new_name)
|
||||
if(!new_name)
|
||||
alert = "You can't give a list variable an empty string for a name!"
|
||||
return
|
||||
if(list_var == new_name)
|
||||
return
|
||||
if(list_vars[list_name])
|
||||
var/list/L = list_vars[list_name]
|
||||
var/ind = L.Find(list_var)
|
||||
if(ind)
|
||||
if(list_vars[list_name][new_name])
|
||||
alert = "There is already a variable named [new_name] in [list_name]!"
|
||||
else
|
||||
var/old_val = list_vars[list_name][list_var]
|
||||
list_vars[list_name][ind] = new_name
|
||||
list_vars[list_name][new_name] = old_val
|
||||
|
||||
/datum/give_sdql_spell/proc/change_list_var_type(list_name, list_var, var_type)
|
||||
if(list_vars[list_name]?[list_var])
|
||||
if(list_vars[list_name][list_var]["type"] == "list" && var_type != "list")
|
||||
purge_list_var("[list_name]/[list_var]")
|
||||
list_vars[list_name][list_var]["type"] = var_type
|
||||
switch(var_type)
|
||||
if("string", "path")
|
||||
list_vars[list_name][list_var]["value"] = ""
|
||||
if("bool", "num")
|
||||
list_vars[list_name][list_var]["value"] = 0
|
||||
if("list")
|
||||
list_vars[list_name][list_var]["value"] = null
|
||||
list_vars |= list("[list_name]/[list_var]" = list())
|
||||
|
||||
/datum/give_sdql_spell/proc/remove_list_var(list_name, list_var)
|
||||
if(list_vars[list_name])
|
||||
var/list/L = list_vars[list_name]
|
||||
var/ind = L.Find(list_var)
|
||||
if(ind)
|
||||
if(list_vars[list_name][list_var]["type"] == "list")
|
||||
purge_list_var("[list_name]/[list_var]")
|
||||
L.Cut(ind, ind+1)
|
||||
list_vars[list_name] = L
|
||||
|
||||
/datum/give_sdql_spell/proc/purge_list_var(list_name)
|
||||
var/ind = list_vars.Find(list_name)
|
||||
if(ind)
|
||||
for(var/V in list_vars[list_name])
|
||||
if(list_vars[list_name][V]["type"] == "list")
|
||||
purge_list_var("[list_name]/[V]")
|
||||
list_vars.Cut(ind, ind+1)
|
||||
|
||||
/datum/give_sdql_spell/proc/generate_list_var(list_name)
|
||||
if(!list_vars[list_name])
|
||||
return null
|
||||
var/list/ret = list()
|
||||
for(var/V in list_vars[list_name])
|
||||
if(list_vars[list_name][V]["type"] == "list")
|
||||
ret[V] = generate_list_var("[list_name]/[V]")
|
||||
else if(list_vars[list_name][V]["type"] == "path")
|
||||
ret[V] = text2path(list_vars[list_name][V]["value"])
|
||||
else if(list_vars[list_name][V]["type"] == "icon")
|
||||
ret[V] = icon(list_vars[list_name][V]["value"])
|
||||
else
|
||||
ret[V] = list_vars[list_name][V]["value"]
|
||||
return ret
|
||||
|
||||
/datum/give_sdql_spell/proc/give_spell()
|
||||
var/path = text2path("/obj/effect/proc_holder/spell/[spell_type]/sdql")
|
||||
var/obj/effect/proc_holder/spell/new_spell = new path(null, target_mob, user.ckey)
|
||||
GLOB.sdql_spells += new_spell
|
||||
reassign_vars(new_spell)
|
||||
new_spell.action.UpdateButtonIcon()
|
||||
if(target_mob.mind)
|
||||
target_mob.mind.AddSpell(new_spell)
|
||||
else
|
||||
target_mob.AddSpell(new_spell)
|
||||
to_chat(user, span_danger("Spells given to mindless mobs will not be transferred in mindswap or cloning!"))
|
||||
return new_spell
|
||||
|
||||
/datum/give_sdql_spell/proc/reassign_vars(obj/effect/proc_holder/spell/target)
|
||||
if(!target)
|
||||
CRASH("edit_spell must be called with a non_null target")
|
||||
var/datum/component/sdql_executor/executor = target.GetComponent(/datum/component/sdql_executor)
|
||||
if(!executor)
|
||||
CRASH("[src]'s SDQL executor component went missing!")
|
||||
for(var/V in saved_vars+list_vars)
|
||||
if(V == "query")
|
||||
executor.vv_edit_var("query", saved_vars["query"])
|
||||
else if(V == "scratchpad")
|
||||
var/list/new_scratchpad = generate_list_var("scratchpad")
|
||||
if(new_scratchpad)
|
||||
executor.vv_edit_var("scratchpad", new_scratchpad)
|
||||
else if(target.vars[V])
|
||||
if(islist(target.vars[V]))
|
||||
if(special_list_vars[V])
|
||||
var/list/overrides_to_save = list_vars[V]
|
||||
executor.saved_overrides[V] = overrides_to_save.Copy()
|
||||
var/list/list_var = generate_list_var(V)
|
||||
if(list_var)
|
||||
target.vv_edit_var(V, list_var)
|
||||
else if(isicon(target.vars[V]))
|
||||
target.vv_edit_var(V, icon(saved_vars[V]))
|
||||
else
|
||||
target.vv_edit_var(V, saved_vars[V])
|
||||
@@ -0,0 +1,146 @@
|
||||
/obj/effect/proc_holder/spell/aimed/sdql
|
||||
name = "Aimed SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
projectile_type = /obj/projectile
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed/sdql/Initialize(mapload, new_owner, giver)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/sdql_executor, giver)
|
||||
RegisterSignal(src, COMSIG_PROJECTILE_ON_HIT, .proc/on_projectile_hit)
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed/sdql/proc/on_projectile_hit(source, firer, target)
|
||||
var/datum/component/sdql_executor/executor = GetComponent(/datum/component/sdql_executor)
|
||||
if(!executor)
|
||||
CRASH("[src]'s SDQL executor component went missing!")
|
||||
executor.execute(list(target), owner.resolve())
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/sdql
|
||||
name = "AoE SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/sdql/Initialize(mapload, new_owner, giver)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/sdql_executor, giver)
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/sdql/cast(list/targets, mob/user)
|
||||
var/datum/component/sdql_executor/executor = GetComponent(/datum/component/sdql_executor)
|
||||
if(!executor)
|
||||
CRASH("[src]'s SDQL executor component went missing!")
|
||||
executor.execute(targets, user)
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/sdql
|
||||
name = "Cone SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
var/list/targets = list()
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/sdql/Initialize(mapload, new_owner, giver)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/sdql_executor, giver)
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/sdql/do_mob_cone_effect(mob/living/target_mob, level)
|
||||
targets |= target_mob
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/sdql/do_obj_cone_effect(obj/target_obj, level)
|
||||
targets |= target_obj
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/sdql/do_turf_cone_effect(turf/target_turf, level)
|
||||
targets |= target_turf
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/sdql/cast(list/targets, mob/user)
|
||||
. = ..()
|
||||
var/datum/component/sdql_executor/executor = GetComponent(/datum/component/sdql_executor)
|
||||
if(!executor)
|
||||
CRASH("[src]'s SDQL executor component went missing!")
|
||||
executor.execute(targets, user)
|
||||
targets = list()
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/sdql
|
||||
name = "Staggered Cone SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
var/list/targets = list()
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/sdql/Initialize(mapload, new_owner, giver)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/sdql_executor, giver)
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/sdql/do_mob_cone_effect(mob/living/target_mob, level)
|
||||
targets |= target_mob
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/sdql/do_obj_cone_effect(obj/target_obj, level)
|
||||
targets |= target_obj
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/sdql/do_turf_cone_effect(turf/target_turf, level)
|
||||
targets |= target_turf
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/sdql/do_cone_effects(list/target_turf_list, level)
|
||||
. = ..()
|
||||
var/datum/component/sdql_executor/executor = GetComponent(/datum/component/sdql_executor)
|
||||
if(!executor)
|
||||
CRASH("[src]'s SDQL executor component went missing!")
|
||||
executor.execute(target_turf_list, owner.resolve())
|
||||
targets = list()
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/sdql
|
||||
name = "Pointed SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/sdql/Initialize(mapload, new_owner, giver)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/sdql_executor, giver)
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/sdql/cast(list/targets, mob/user)
|
||||
var/datum/component/sdql_executor/executor = GetComponent(/datum/component/sdql_executor)
|
||||
if(!executor)
|
||||
CRASH("[src]'s SDQL executor component went missing!")
|
||||
executor.execute(targets, user)
|
||||
|
||||
/obj/effect/proc_holder/spell/self/sdql
|
||||
name = "Self SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
|
||||
/obj/effect/proc_holder/spell/self/sdql/Initialize(mapload, new_owner, giver)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/sdql_executor, giver)
|
||||
|
||||
/obj/effect/proc_holder/spell/self/sdql/cast(list/targets, mob/user)
|
||||
var/datum/component/sdql_executor/executor = GetComponent(/datum/component/sdql_executor)
|
||||
if(!executor)
|
||||
CRASH("[src]'s SDQL executor component went missing!")
|
||||
executor.execute(targets, user)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sdql
|
||||
name = "Targeted SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sdql/Initialize(mapload, new_owner, giver)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/sdql_executor, giver)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sdql/cast(list/targets, mob/user)
|
||||
var/datum/component/sdql_executor/executor = GetComponent(/datum/component/sdql_executor)
|
||||
if(!executor)
|
||||
CRASH("[src]'s SDQL executor component went missing!")
|
||||
executor.execute(targets, user)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/touch/sdql
|
||||
name = "Touch SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
var/list/hand_var_overrides = list() //The touch attack has its vars changed to the ones put in this list.
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/touch/sdql/Initialize(mapload, new_owner, giver)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/sdql_executor, giver)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/touch/sdql/ChargeHand(mob/living/carbon/user)
|
||||
if(..())
|
||||
for(var/V in hand_var_overrides)
|
||||
if(attached_hand.vars[V])
|
||||
attached_hand.vv_edit_var(V, hand_var_overrides[V])
|
||||
RegisterSignal(attached_hand, COMSIG_ITEM_AFTERATTACK, .proc/on_touch_attack)
|
||||
user.update_inv_hands()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/touch/sdql/proc/on_touch_attack(source, target, user)
|
||||
var/datum/component/sdql_executor/executor = GetComponent(/datum/component/sdql_executor)
|
||||
if(!executor)
|
||||
CRASH("[src]'s SDQL executor component went missing!")
|
||||
executor.execute(list(target), user)
|
||||
@@ -1,848 +0,0 @@
|
||||
/obj/effect/proc_holder/spell/aimed/sdql
|
||||
name = "Aimed SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
var/query = "CALL visible_message(\"<span class='warning'>The spell fizzles!</span>\") ON * IN TARGETS"
|
||||
var/list/scratchpad = list() //Use this to store vars in between queries and casts.
|
||||
projectile_type = /obj/projectile/sdql
|
||||
|
||||
/obj/effect/proc_holder/spell/aimed/sdql/ready_projectile(obj/projectile/P, atom/target, mob/user, iteration)
|
||||
var/obj/projectile/sdql/S = P
|
||||
S.linked_spell = src
|
||||
S.query = query
|
||||
|
||||
/obj/projectile/sdql
|
||||
name = "\improper SDQL projectile"
|
||||
damage_type = STAMINA
|
||||
nodamage = TRUE
|
||||
damage = 0
|
||||
var/query
|
||||
var/obj/effect/proc_holder/spell/linked_spell
|
||||
|
||||
/obj/projectile/sdql/on_hit(atom/target, blocked, pierce_hit)
|
||||
. = ..()
|
||||
var/mob/firer_mob = firer
|
||||
process_spell_query(query, list(target), firer_mob, linked_spell)
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/sdql
|
||||
name = "AoE SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
var/query = "CALL visible_message(\"<span class='warning'>The spell fizzles!</span>\") ON * IN TARGETS"
|
||||
var/list/scratchpad = list() //Use this to store vars in between queries and casts.
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/sdql/cast(list/targets, mob/user)
|
||||
process_spell_query(query, targets, user, src)
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/sdql
|
||||
name = "Cone SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
var/query = "CALL visible_message(\"<span class='warning'>The spell fizzles!</span>\") ON * IN TARGETS"
|
||||
var/list/targets = list()
|
||||
var/list/scratchpad = list() //Use this to store vars in between queries and casts.
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/sdql/do_mob_cone_effect(mob/living/target_mob, level)
|
||||
targets |= target_mob
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/sdql/do_obj_cone_effect(obj/target_obj, level)
|
||||
targets |= target_obj
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/sdql/do_turf_cone_effect(turf/target_turf, level)
|
||||
targets |= target_turf
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/sdql/cast(list/targets, mob/user)
|
||||
. = ..()
|
||||
process_spell_query(query, targets, user, src)
|
||||
targets = list()
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/sdql
|
||||
name = "Staggered Cone SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
var/query = "CALL visible_message(\"<span class='warning'>The spell fizzles!</span>\") ON * IN TARGETS"
|
||||
var/list/targets = list()
|
||||
var/list/scratchpad = list() //Use this to store vars in between queries and casts.
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/sdql/do_mob_cone_effect(mob/living/target_mob, level)
|
||||
targets |= target_mob
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/sdql/do_obj_cone_effect(obj/target_obj, level)
|
||||
targets |= target_obj
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/sdql/do_turf_cone_effect(turf/target_turf, level)
|
||||
targets |= target_turf
|
||||
|
||||
/obj/effect/proc_holder/spell/cone/staggered/sdql/do_cone_effects(list/target_turf_list, level)
|
||||
. = ..()
|
||||
process_spell_query(query, targets, usr, src)
|
||||
targets = list()
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/sdql
|
||||
name = "Pointed SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
var/query = "CALL visible_message(\"<span class='warning'>The spell fizzles!</span>\") ON * IN TARGETS"
|
||||
var/list/scratchpad = list() //Use this to store vars in between queries and casts.
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/sdql/cast(list/targets, mob/user)
|
||||
process_spell_query(query, targets, user, src)
|
||||
|
||||
/obj/effect/proc_holder/spell/self/sdql
|
||||
name = "Self SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
var/query = "CALL visible_message(\"<span class='warning'>The spell fizzles!</span>\") ON * IN TARGETS"
|
||||
var/list/scratchpad = list() //Use this to store vars in between queries and casts.
|
||||
|
||||
/obj/effect/proc_holder/spell/self/sdql/cast(list/targets, mob/user)
|
||||
process_spell_query(query, list(user), user, src)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sdql
|
||||
name = "Targeted SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
var/query = "CALL visible_message(\"<span class='warning'>The spell fizzles!</span>\") ON * IN TARGETS"
|
||||
var/list/scratchpad = list() //Use this to store vars in between queries and casts.
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/sdql/cast(list/targets, mob/user)
|
||||
process_spell_query(query, targets, user, src)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/touch/sdql
|
||||
name = "Touch SDQL Spell"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
hand_path = /obj/item/melee/touch_attack/sdql
|
||||
var/query = "CALL visible_message(\"<span class='warning'>The spell fizzles!</span>\") ON * IN TARGETS"
|
||||
var/list/scratchpad = list() //Use this to store vars in between queries and casts.
|
||||
var/list/hand_var_overrides = list() //The touch attack has its vars changed to the ones put in this list.
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/touch/sdql/ChargeHand(mob/living/carbon/user)
|
||||
if(..())
|
||||
for(var/V in hand_var_overrides)
|
||||
if(V in attached_hand.vars)
|
||||
attached_hand.vv_edit_var(V, hand_var_overrides[V])
|
||||
user.update_inv_hands()
|
||||
|
||||
|
||||
/obj/item/melee/touch_attack/sdql
|
||||
name = "\improper SDQL touch attack"
|
||||
desc = "If you are reading this outside of the \"Give SDQL Spell\" menu, tell the admin that gave this spell to you to use said menu."
|
||||
catchphrase = "ADMINS WERE LAZY!!"
|
||||
|
||||
/obj/item/melee/touch_attack/sdql/afterattack(atom/target, mob/user, proximity)
|
||||
var/obj/effect/proc_holder/spell/targeted/touch/sdql/spell = attached_spell
|
||||
process_spell_query(spell.query, list(target), user, spell)
|
||||
. = ..()
|
||||
|
||||
//Returns the address of x without the square brackets around it.
|
||||
#define RAW_ADDRESS(x) copytext("\ref[x]",2,-1)
|
||||
|
||||
/proc/process_spell_query(query_text, list/targets, mob/user, source)
|
||||
if(!CONFIG_GET(flag/sdql_spells))
|
||||
return
|
||||
if(!length(query_text))
|
||||
return
|
||||
var/message_query = query_text
|
||||
var/list/targets_and_user_list = targets+user
|
||||
var/targets_and_user_string = ref_list(targets_and_user_list)
|
||||
var/targets_string = ref_list(targets)
|
||||
query_text = replacetextEx_char(query_text, "TARGETS_AND_USER", "[targets_and_user_string]")
|
||||
message_query = replacetext_char(message_query, "TARGETS_AND_USER", (targets_and_user_list.len > 3) ? "\[<i>[targets_and_user_list.len] items</i>]" : targets_and_user_string)
|
||||
query_text = replacetextEx_char(query_text, "USER", "{[RAW_ADDRESS(user)]}")
|
||||
message_query = replacetextEx_char(message_query, "USER", "{[RAW_ADDRESS(user)]}")
|
||||
query_text = replacetextEx_char(query_text, "TARGETS", "[targets_string]")
|
||||
message_query = replacetextEx_char(message_query, "TARGETS", (targets.len > 3) ? "\[<i>[targets.len] items</i>]" : targets_string)
|
||||
query_text = replacetextEx_char(query_text, "SOURCE", "{[RAW_ADDRESS(source)]}")
|
||||
message_query = replacetextEx_char(message_query, "SOURCE", "{[RAW_ADDRESS(source)]}")
|
||||
var/query_message = "[key_name(user)] executed SDQL query(s): \"[message_query]\" using a player query caller."
|
||||
message_admins(query_message)
|
||||
log_game(query_message)
|
||||
var/list/query_list = SDQL2_tokenize(query_text)
|
||||
if(!query_list.len)
|
||||
return
|
||||
var/list/querys = SDQL_parse(query_list)
|
||||
if(!querys.len)
|
||||
return
|
||||
|
||||
var/list/datum/sdql2_query/running = list()
|
||||
var/list/datum/sdql2_query/waiting_queue = list() //Sequential queries queue.
|
||||
|
||||
for(var/list/query_tree in querys)
|
||||
var/datum/sdql2_query/query = new /datum/sdql2_query(query_tree, SU = TRUE, admin_interact = FALSE)
|
||||
if(QDELETED(query))
|
||||
continue
|
||||
waiting_queue += query
|
||||
|
||||
var/datum/sdql2_query/query = popleft(waiting_queue)
|
||||
running += query
|
||||
query.ARun()
|
||||
|
||||
var/finished = FALSE
|
||||
do
|
||||
CHECK_TICK
|
||||
finished = TRUE
|
||||
for(var/i in running)
|
||||
query = i
|
||||
if(QDELETED(query))
|
||||
running -= query
|
||||
continue
|
||||
else if(query.state != SDQL2_STATE_IDLE)
|
||||
finished = FALSE
|
||||
if(query.state == SDQL2_STATE_ERROR)
|
||||
running -= query
|
||||
else
|
||||
if(query.finished)
|
||||
qdel(query)
|
||||
if(waiting_queue.len)
|
||||
finished = FALSE
|
||||
var/datum/sdql2_query/next_query = popleft(waiting_queue)
|
||||
running += next_query
|
||||
next_query.ARun()
|
||||
else
|
||||
running -= query
|
||||
while(!finished)
|
||||
|
||||
/proc/ref_list(list/L)
|
||||
if(!L.len)
|
||||
return "\[]"
|
||||
var/ret = "\["
|
||||
for(var/i in 1 to L.len-1)
|
||||
ret += "{[RAW_ADDRESS(L[i])]},"
|
||||
ret += "{[RAW_ADDRESS(L[L.len])]}]"
|
||||
return ret
|
||||
|
||||
#undef RAW_ADDRESS
|
||||
|
||||
/client/proc/cmd_give_sdql_spell(mob/target in GLOB.mob_list)
|
||||
set name = "Give SDQL spell"
|
||||
set hidden = TRUE
|
||||
if(CONFIG_GET(flag/sdql_spells))
|
||||
var/datum/give_sdql_spell/ui = new(usr, target)
|
||||
ui.ui_interact(usr)
|
||||
else
|
||||
to_chat(usr, span_warning("SDQL spells are disabled."))
|
||||
|
||||
|
||||
/datum/give_sdql_spell
|
||||
var/client/user
|
||||
var/mob/living/target_mob
|
||||
var/spell_type
|
||||
var/list/saved_vars = list()
|
||||
var/list/list_vars = list("scratchpad" = list())
|
||||
var/alert
|
||||
|
||||
//This list contains all the vars that it should be okay to edit from the menu
|
||||
var/static/list/editable_spell_vars = list(
|
||||
"action_background_icon_state",
|
||||
"action_icon_state",
|
||||
"action_icon",
|
||||
"active_msg",
|
||||
"aim_assist",
|
||||
"antimagic_allowed",
|
||||
"base_icon_state",
|
||||
"centcom_cancast",
|
||||
"charge_max",
|
||||
"charge_type",
|
||||
"clothes_req",
|
||||
"cone_level",
|
||||
"cult_req",
|
||||
"deactive_msg",
|
||||
"desc",
|
||||
"drawmessage",
|
||||
"dropmessage",
|
||||
"hand_var_overrides",
|
||||
"holder_var_amount",
|
||||
"holder_var_type",
|
||||
"human_req",
|
||||
"include_user",
|
||||
"inner_radius",
|
||||
"invocation_emote_self",
|
||||
"invocation_type",
|
||||
"invocation",
|
||||
"max_targets",
|
||||
"message",
|
||||
"name",
|
||||
"nonabstract_req",
|
||||
"overlay_icon_state",
|
||||
"overlay_icon",
|
||||
"overlay_lifespan",
|
||||
"overlay",
|
||||
"phase_allowed",
|
||||
"player_lock",
|
||||
"projectile_amount",
|
||||
"projectile_var_overrides",
|
||||
"projectiles_per_fire",
|
||||
"query",
|
||||
"random_target_priority",
|
||||
"random_target",
|
||||
"range",
|
||||
"ranged_mousepointer",
|
||||
"respect_density",
|
||||
"selection_type",
|
||||
"self_castable",
|
||||
"smoke_amt",
|
||||
"smoke_spread",
|
||||
"sound",
|
||||
"sparks_amt",
|
||||
"sparks_spread",
|
||||
"stat_allowed",
|
||||
"still_recharging_message",
|
||||
"target_ignore_prev",
|
||||
)
|
||||
|
||||
//If a spell creates a datum with vars it overrides, this list should contain an association with the supertype of the created datum
|
||||
var/static/list/special_list_vars = list(
|
||||
"projectile_var_overrides" = list(
|
||||
"supertype" = /obj/projectile,
|
||||
"type" = /obj/projectile/sdql,
|
||||
),
|
||||
"hand_var_overrides" = list(
|
||||
"supertype" = /obj/item/melee/touch_attack,
|
||||
"type" = /obj/item/melee/touch_attack/sdql,
|
||||
),
|
||||
)
|
||||
|
||||
var/static/list/static_data
|
||||
|
||||
//base64 representations of any icons that may need to be displayed
|
||||
var/action_icon_base64
|
||||
var/projectile_icon_base64
|
||||
var/hand_icon_base64
|
||||
var/overlay_icon_base64
|
||||
var/mouse_icon_base64
|
||||
|
||||
/datum/give_sdql_spell/New(_user, target)
|
||||
if(!CONFIG_GET(flag/sdql_spells))
|
||||
to_chat(_user, span_warning("SDQL spells are disabled."))
|
||||
qdel(src)
|
||||
return
|
||||
user = CLIENT_FROM_VAR(_user)
|
||||
|
||||
if(!isliving(target))
|
||||
alert("Invalid mob")
|
||||
return
|
||||
target_mob = target
|
||||
|
||||
/datum/give_sdql_spell/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "SDQLSpellMenu", "Give SDQL Spell")
|
||||
ui.open()
|
||||
ui.set_autoupdate(FALSE)
|
||||
|
||||
/datum/give_sdql_spell/ui_state(mob/user)
|
||||
return GLOB.admin_state
|
||||
|
||||
/datum/give_sdql_spell/ui_status(mob/user, datum/ui_state/state)
|
||||
if(QDELETED(target_mob))
|
||||
return UI_CLOSE
|
||||
return ..()
|
||||
|
||||
/datum/give_sdql_spell/ui_close(mob/user)
|
||||
qdel(src)
|
||||
|
||||
/datum/give_sdql_spell/ui_data(mob/user, params)
|
||||
var/list/data = list()
|
||||
data["type"] = spell_type
|
||||
data["saved_vars"] = saved_vars
|
||||
data["list_vars"] = list_vars
|
||||
data["action_icon"] = action_icon_base64
|
||||
data["projectile_icon"] = projectile_icon_base64
|
||||
data["hand_icon"] = hand_icon_base64
|
||||
data["overlay_icon"] = overlay_icon_base64
|
||||
data["mouse_icon"] = mouse_icon_base64
|
||||
data["alert"] = alert
|
||||
alert = ""
|
||||
return data
|
||||
|
||||
/datum/give_sdql_spell/ui_static_data(mob/user)
|
||||
if(!static_data)
|
||||
static_data = list(
|
||||
"types" = list("aimed", "aoe_turf", "cone", "cone/staggered", "pointed", "self", "targeted", "targeted/touch"),
|
||||
"tooltips" = list(
|
||||
"query" = "The SDQL query that is executed. Certain keywords are specific to SDQL spell queries.\n\
|
||||
$type\n\
|
||||
USER is replaced with a reference to the user of the spell.\n\
|
||||
TARGETS_AND_USER is replaced with the combined references from TARGETS and USER.\n\
|
||||
SOURCE is replaced with a reference to this spell, allowing you to refer to and edit variables within it.\n\
|
||||
You can use the list variable \"scratchpad\" to store variables between individual queries within the same cast or between multiple casts.",
|
||||
"query_aimed" = "TARGETS is replaced with a list containing a reference to the atom hit by the fired projectile.",
|
||||
"query_aoe_turf" = "TARGETS is replaced with a list containing references to every atom in the spell's area of effect.",
|
||||
"query_cone" = "TARGETS is replaced with a list containing references to every atom in the cone produced by the spell.",
|
||||
"query_cone/staggered" = "The query will be executed once for every level of the cone produced by the spell.\n\
|
||||
TARGETS is replaced with a list containing references to every atom in the given level of the cone.",
|
||||
"query_pointed" = "TARGETS is replaced with a list containing a reference to the targeted atom.",
|
||||
"query_self" = "TARGETS is replaced with a list containing a reference to the caster.",
|
||||
"query_targeted" = "TARGETS is replaced with a list containing a reference(s) to the targeted mob(s).",
|
||||
"query_targeted_touch" = "TARGETS is replaced with a list containing a reference to the atom hit with the touch attack.",
|
||||
"charge_type" = "How the spell's charge works. This affects how charge_max is used.\n\
|
||||
When set to \"recharge\", charge_max is the time in deciseconds between casts of the spell.\n\
|
||||
When set to \"charges\", the user can only use the spell a number of times equal to charge_max.\n\
|
||||
When set to \"holder_var\", charge_max is not used. holder_var_type and holder_var_amount are used instead.\n",
|
||||
"holder_var_type" = "When charge_type is set to \"holder_var\", this is the name of the var that is modified each time the spell is cast.\n\
|
||||
If this is set to \"bruteloss\", \"fireloss\", \"toxloss\", or \"oxyloss\", the user will take the corresponding damage.\n\
|
||||
If this is set to \"stun\", \"knockdown\", \"paralyze\", \"immobilize\", or \"unconscious\", the user will suffer the corresponding status effect.\n\
|
||||
If this is set to anything else, the variable with the appropriate name will be modified.",
|
||||
"holder_var_amount" = "The amount of damage taken, the duration of status effect inflicted, or the change made to any other variable.",
|
||||
"clothes_req" = "Whether the user has to be wearing wizard robes to cast the spell.",
|
||||
"cult_req" = "Whether the user has to be wearing cult robes to cast the spell.",
|
||||
"human_req" = "Whether the user has to be a human to cast the spell. Redundant when clothes_req is true.",
|
||||
"nonabstract_req" = "If this is true, the spell cannot be cast by brains and pAIs.",
|
||||
"stat_allowed" = "Whether the spell can be cast if the user is unconscious or dead.",
|
||||
"phase_allowed" = "Whether the spell can be cast while the user is jaunting or bloodcrawling.",
|
||||
"antimagic_allowed" = "Whether the spell can be cast while the user is affected by anti-magic effects.",
|
||||
"invocation_type" = "How the spell is invoked.\n\
|
||||
When set to \"whisper\", the user whispers the invocation, as if with the whisper verb.\n\
|
||||
When set to \"shout\", the user says the invocation, as if with the say verb.\n\
|
||||
When set to \"emote\", a visible message is produced.",
|
||||
"invocation" = "What the user says, whispers, or emotes when using the spell.",
|
||||
"invocation_emote_self" = "What the user sees in their own chat when they use the spell.",
|
||||
"selection_type" = "Whether the spell can target any mob in range, or only visible mobs in range.",
|
||||
"range" = "The spell's range, in tiles.",
|
||||
"message" = "What mobs affected by the spell see in their chat.\n\
|
||||
Keep in mind, just because a mob is affected by the spell doesn't mean the query will have any effect on them.",
|
||||
"player_lock" = "If false, simple mobs can use the spell.",
|
||||
"overlay" = "Whether an overlay is drawn atop atoms affectecd by the spell.\n\
|
||||
Keep in mind, just because an atom is affected by the spell doesn't mean the query will have any effect on it.",
|
||||
"overlay_lifetime" = "The amount of time in deciseconds the overlay will persist.",
|
||||
"sparks_spread" = "Whether the spell produces sparks when cast.",
|
||||
"smoke_spread" = "The kind of smoke, if any, the spell produces when cast.",
|
||||
"centcom_cancast" = "If true, the spell can be cast on the centcom Z-level.",
|
||||
"max_targets" = "The maximum number of mobs the spell can target.",
|
||||
"target_ignore_prev" = "If false, the same mob can be targeted multiple times.",
|
||||
"include_user" = "If true, the user can target themselves with the spell.",
|
||||
"random_target" = "If true, the spell will target a random mob(s) in range.",
|
||||
"random_target_priority" = "Whether the spell will target random mobs in range or the closest mobs in range.",
|
||||
"inner_radius" = "If this is a non-negative number, the spell will not affect atoms within that many tiles of the user.",
|
||||
"ranged_mousepointer" = "The icon used for the mouse when aiming the spell.",
|
||||
"deactive_mesg" = "The message the user sees when canceling the spell.",
|
||||
"active_msg" = "The message the user sees when activating the spell.",
|
||||
"projectile_amount" = "The maximum number of projectiles the user can fire with each cast of the spell.",
|
||||
"projectiles_per_fire" = "The amount of projectiles fired with each click of the mouse.",
|
||||
"projectile_var_overrides" = "The fired projectiles will have the appropriate variables overridden by the corresponding values in this associative list.\n\
|
||||
You should probably set \"name\", \"icon\", and \"icon_state\".\n\
|
||||
Refer to code/modules/projectiles/projectile.dm to see what other vars you can override.",
|
||||
"cone_level" = "How many tiles out the cone will extend.",
|
||||
"respect_density" = "If true, the cone produced by the spell is blocked by walls.",
|
||||
"self_castable" = "If true, the user can cast the spell on themselves.",
|
||||
"aim_assist" = "If true, the spell has turf-based aim assist.",
|
||||
"drawmessage" = "The message the user sees when activating the spell.",
|
||||
"dropmessage" = "The message the user sees when canceling the spell.",
|
||||
"hand_var_overrides" = "The touch attack will have the appropriate variables overridden by the corresponding values in this associative list.\n\
|
||||
You should probably set \"name\", \"desc\", \"catchphrase\", \"on_use_sound\" \"icon\", \"icon_state\", and \"inhand_icon_state\".\n\
|
||||
Refer to code/modules/spells/spell_types/godhand.dm for see what other vars you can override.",
|
||||
"scratchpad" = "This list can be used to store variables between individual queries within the same cast or between casts.\n\
|
||||
You can declare variables from this menu for convenience. To access this list in a query, use the identifier \"SOURCE.scratchpad\".\n\
|
||||
Refer to the _list procs defined in code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm for information on how to modify and edit list vars from within a query.",
|
||||
),
|
||||
)
|
||||
return static_data
|
||||
|
||||
#define LIST_VAR_FLAGS_TYPED 1
|
||||
#define LIST_VAR_FLAGS_NAMED 2
|
||||
|
||||
/datum/give_sdql_spell/ui_act(action, params, datum/tgui/ui)
|
||||
if(..())
|
||||
return
|
||||
. = TRUE
|
||||
switch(action)
|
||||
if("type")
|
||||
spell_type = params["path"]
|
||||
var/path = text2path("/obj/effect/proc_holder/spell/[spell_type]/sdql")
|
||||
var/datum/sample = new path
|
||||
if(spell_type)
|
||||
for(var/V in sample.vars)
|
||||
if(V in editable_spell_vars)
|
||||
if(islist(sample.vars[V]))
|
||||
list_vars += list("[V]" = list())
|
||||
if(V in special_list_vars)
|
||||
var/subpath = special_list_vars[V]["type"]
|
||||
var/datum/subsample = new subpath
|
||||
if("icon" in sample.vars)
|
||||
icon_needs_updating("[V]/icon")
|
||||
qdel(subsample)
|
||||
else
|
||||
saved_vars[V] = sample.vars[V]
|
||||
icon_needs_updating(V)
|
||||
qdel(sample)
|
||||
if("variable")
|
||||
var/V = params["name"]
|
||||
if(V == "holder_var_type")
|
||||
if(!holder_var_validate(params["value"]))
|
||||
return
|
||||
saved_vars[V] = params["value"]
|
||||
icon_needs_updating(V)
|
||||
if("bool_variable")
|
||||
saved_vars[params["name"]] = !saved_vars[params["name"]]
|
||||
if("list_variable_add")
|
||||
if(params["list"] in list_vars)
|
||||
if(params["list"] in special_list_vars)
|
||||
var/superpath = special_list_vars[params["list"]]["supertype"]
|
||||
var/path = special_list_vars[params["list"]]["type"]
|
||||
var/datum/supersample = new superpath
|
||||
var/datum/sample = new path
|
||||
var/list/choosable_vars = map_var_list((sample.vars&supersample.vars)-list_vars[params["list"]], sample)
|
||||
var/chosen_var = input(user, "Select variable to add.", "Add SDQL Spell", null) as null|anything in sortList(choosable_vars)
|
||||
if(chosen_var)
|
||||
if(islist(sample.vars[choosable_vars[chosen_var]]))
|
||||
list_vars[params["list"]] += list("[choosable_vars[chosen_var]]" = list("type" = "list", "value" = null, "flags" = LIST_VAR_FLAGS_TYPED|LIST_VAR_FLAGS_NAMED))
|
||||
list_vars |= list("[params["list"]]/[choosable_vars[chosen_var]]" = list())
|
||||
else if(isnum(sample.vars[choosable_vars[chosen_var]]))
|
||||
list_vars[params["list"]][choosable_vars[chosen_var]] = list("type" = "num", "value" = sample.vars[choosable_vars[chosen_var]], "flags" = LIST_VAR_FLAGS_TYPED|LIST_VAR_FLAGS_NAMED)
|
||||
else if(ispath(sample.vars[choosable_vars[chosen_var]]))
|
||||
list_vars[params["list"]][choosable_vars[chosen_var]] = list("type" = "path", "value" = sample.vars[choosable_vars[chosen_var]], "flags" = LIST_VAR_FLAGS_TYPED|LIST_VAR_FLAGS_NAMED)
|
||||
else if(isicon(sample.vars[choosable_vars[chosen_var]]))
|
||||
list_vars[params["list"]][choosable_vars[chosen_var]] = list("type" = "icon", "value" = sample.vars[choosable_vars[chosen_var]], "flags" = LIST_VAR_FLAGS_TYPED|LIST_VAR_FLAGS_NAMED)
|
||||
else if(istext(sample.vars[choosable_vars[chosen_var]]) || isfile(sample.vars[choosable_vars[chosen_var]]))
|
||||
list_vars[params["list"]][choosable_vars[chosen_var]] = list("type" = "string", "value" = sample.vars[choosable_vars[chosen_var]], "flags" = LIST_VAR_FLAGS_TYPED|LIST_VAR_FLAGS_NAMED)
|
||||
else if(isnull(sample.vars[choosable_vars[chosen_var]]))
|
||||
list_vars[params["list"]][choosable_vars[chosen_var]] = list("type" = "num", "value" = 0, "flags" = LIST_VAR_FLAGS_NAMED)
|
||||
alert = "Could not determine the type for [params["list"]]/[choosable_vars[chosen_var]]! Be sure to set it correctly, or you may cause unnecessary runtimes!"
|
||||
else
|
||||
alert = "[params["list"]]/[choosable_vars[chosen_var]] is not of a supported type!"
|
||||
icon_needs_updating("[params["list"]]/[choosable_vars[chosen_var]]")
|
||||
qdel(sample)
|
||||
qdel(supersample)
|
||||
else
|
||||
if(!("new_var" in list_vars[params["list"]]))
|
||||
list_vars[params["list"]] += list("new_var" = list("type" = "num", "value" = 0, "flags" = 0))
|
||||
else
|
||||
alert = "Rename or remove [params["list"]]/new_var before attempting to add another variable to this list!"
|
||||
if("list_variable_remove")
|
||||
remove_list_var(params["list"], params["name"])
|
||||
if("list_variable_rename")
|
||||
rename_list_var(params["list"], params["name"], params["new_name"])
|
||||
if("list_variable_change_type")
|
||||
change_list_var_type(params["list"], params["name"], params["value"])
|
||||
if("list_variable_change_value")
|
||||
set_list_var(params["list"], params["name"], params["value"])
|
||||
icon_needs_updating("[params["list"]]/[params["name"]]")
|
||||
if("list_variable_change_bool")
|
||||
toggle_list_var(params["list"], params["name"])
|
||||
if("save")
|
||||
var/f = file("data/TempSpellUpload")
|
||||
fdel(f)
|
||||
WRITE_FILE(f, json_encode(list("type" = spell_type, "vars" = saved_vars, "list_vars" = list_vars)))
|
||||
user << ftp(f,"[replacetext_char(saved_vars["name"], " ", "_")].json")
|
||||
if("load")
|
||||
var/spell_file = input("Pick spell json file:", "File") as null|file
|
||||
if(!spell_file)
|
||||
return
|
||||
var/filedata = file2text(spell_file)
|
||||
var/json = json_decode(filedata)
|
||||
if(!json)
|
||||
alert = "JSON decode error!"
|
||||
return
|
||||
if(load_from_json(json))
|
||||
icon_needs_updating("everything")
|
||||
else
|
||||
alert = "Malformed/Outdated file!"
|
||||
return
|
||||
if("confirm")
|
||||
give_spell()
|
||||
ui.close()
|
||||
|
||||
/datum/give_sdql_spell/proc/load_from_json(json)
|
||||
if(!(("type" in json) && ("vars" in json) && ("list_vars" in json)))
|
||||
return FALSE
|
||||
var/temp_type = json["type"]
|
||||
var/datum/D = text2path("/obj/effect/proc_holder/spell/[temp_type]/sdql")
|
||||
if(!ispath(D))
|
||||
return FALSE
|
||||
if(!islist(json["vars"]))
|
||||
return FALSE
|
||||
if(!islist(json["list_vars"]))
|
||||
return FALSE
|
||||
var/list/temp_vars = json["vars"]
|
||||
var/list/temp_list_vars = json["list_vars"]
|
||||
D = new D
|
||||
. = TRUE
|
||||
for(var/V in temp_vars)
|
||||
if(!istext(V))
|
||||
. = FALSE
|
||||
break
|
||||
if(!(V in editable_spell_vars))
|
||||
. = FALSE
|
||||
break
|
||||
if(!(V in D.vars))
|
||||
. = FALSE
|
||||
break
|
||||
if(islist(D.vars[V]))
|
||||
. = FALSE
|
||||
break
|
||||
if(istext(D.vars[V]) || isicon(D.vars[V]) || ispath(D.vars[V]))
|
||||
if(!istext(temp_vars[V]))
|
||||
. = FALSE
|
||||
break
|
||||
if(isnum(D.vars[V]))
|
||||
if(!isnum(temp_vars[V]))
|
||||
. = FALSE
|
||||
break
|
||||
if(.)
|
||||
for(var/V in temp_list_vars)
|
||||
if(!islist(temp_list_vars[V]))
|
||||
. = FALSE
|
||||
break
|
||||
if((V in special_list_vars) && (V in D.vars))
|
||||
var/datum/sample = special_list_vars[V]["type"]
|
||||
sample = new sample
|
||||
for(var/W in temp_list_vars[V])
|
||||
if(!istext(W))
|
||||
. = FALSE
|
||||
break
|
||||
if(!islist(temp_list_vars[V][W]))
|
||||
. = FALSE
|
||||
break
|
||||
if(!(("type" in temp_list_vars[V][W]) && ("value" in temp_list_vars[V][W]) && ("flags" in temp_list_vars[V][W])))
|
||||
. = FALSE
|
||||
break
|
||||
if(!isnum(temp_list_vars[V][W]["flags"]) || (temp_list_vars[V][W]["flags"] & LIST_VAR_FLAGS_TYPED|LIST_VAR_FLAGS_NAMED) == LIST_VAR_FLAGS_TYPED)
|
||||
. = FALSE
|
||||
break
|
||||
if(!istext(temp_list_vars[V][W]["type"]))
|
||||
. = FALSE
|
||||
break
|
||||
if(!(temp_list_vars[V][W]["flags"] & LIST_VAR_FLAGS_TYPED))
|
||||
if(!isnull(sample.vars[W]))
|
||||
. = FALSE
|
||||
break
|
||||
else
|
||||
switch(temp_list_vars[V][W]["type"])
|
||||
if("list")
|
||||
if(!islist(sample.vars[W]))
|
||||
. = FALSE
|
||||
break
|
||||
if(!("[V]/[W]" in temp_list_vars))
|
||||
. = FALSE
|
||||
break
|
||||
if("num")
|
||||
if(isnum(temp_list_vars[V][W]["value"]))
|
||||
if(!(isnum(sample.vars[W])))
|
||||
. = FALSE
|
||||
break
|
||||
else
|
||||
. = FALSE
|
||||
break
|
||||
if("string")
|
||||
if(istext(temp_list_vars[V][W]["value"]))
|
||||
if(!(istext(sample.vars[W]) || isfile(sample.vars[W])))
|
||||
. = FALSE
|
||||
break
|
||||
else
|
||||
. = FALSE
|
||||
break
|
||||
if("path")
|
||||
if(istext(temp_list_vars[V][W]["value"]))
|
||||
if(!(ispath(sample.vars[W])))
|
||||
. = FALSE
|
||||
break
|
||||
else
|
||||
. = FALSE
|
||||
break
|
||||
if("icon")
|
||||
if(istext(temp_list_vars[V][W]["value"]))
|
||||
if(!(isicon(sample.vars[W])))
|
||||
. = FALSE
|
||||
break
|
||||
else
|
||||
. = FALSE
|
||||
break
|
||||
qdel(sample)
|
||||
if(!.)
|
||||
break
|
||||
qdel(D)
|
||||
if(.)
|
||||
spell_type = temp_type
|
||||
saved_vars = temp_vars
|
||||
list_vars = temp_list_vars
|
||||
|
||||
#undef LIST_VAR_FLAGS_TYPED
|
||||
#undef LIST_VAR_FLAGS_NAMED
|
||||
|
||||
/datum/give_sdql_spell/proc/map_var_list(list/L, datum/D)
|
||||
var/list/ret = list()
|
||||
for(var/V in L)
|
||||
if(V in D.vars)
|
||||
ret["[V] = [string_rep(D.vars[V])]"] = V
|
||||
return ret
|
||||
|
||||
/datum/give_sdql_spell/proc/string_rep(V)
|
||||
if(istext(V) || isfile(V) || isicon(V))
|
||||
return "\"[V]\""
|
||||
else if(isnull(V))
|
||||
return "null"
|
||||
else
|
||||
return "[V]"
|
||||
|
||||
/datum/give_sdql_spell/proc/holder_var_validate(V)
|
||||
switch(V)
|
||||
if("bruteloss", "fireloss", "toxloss", "oxyloss", "stun", "knockdown", "paralyze", "unconscious")
|
||||
return TRUE
|
||||
else
|
||||
if(V in target_mob.vars)
|
||||
if(!isnum(target_mob.vars[V]))
|
||||
alert = "[target_mob.type]/[V] is not a number!"
|
||||
return FALSE
|
||||
else
|
||||
return TRUE
|
||||
else
|
||||
alert = "[target_mob.type] has no such variable [V]!"
|
||||
return FALSE
|
||||
|
||||
/datum/give_sdql_spell/proc/icon_needs_updating(var_name)
|
||||
switch(var_name)
|
||||
if("action_icon", "action_icon_state", "action_background_icon_state")
|
||||
var/icon/out_icon = icon('icons/effects/effects.dmi', "nothing")
|
||||
var/image/out_image = image('icons/mob/actions/backgrounds.dmi', null, saved_vars["action_background_icon_state"])
|
||||
var/overlay_icon = icon(saved_vars["action_icon"], saved_vars["action_icon_state"])
|
||||
out_image.overlays += image(overlay_icon)
|
||||
out_icon.Insert(getFlatIcon(out_image, no_anim = TRUE))
|
||||
action_icon_base64 = icon2base64(out_icon)
|
||||
|
||||
if("projectile_var_overrides/icon", "projectile_var_overrides/icon_state")
|
||||
var/atom/A = /obj/projectile/sdql
|
||||
var/icon = initial(A.icon)
|
||||
var/icon_state = initial(A.icon_state)
|
||||
if(list_vars["projectile_var_overrides"]["icon"])
|
||||
icon = list_vars["projectile_var_overrides"]["icon"]["value"]
|
||||
if(list_vars["projectile_var_overrides"]["icon_state"])
|
||||
icon_state = list_vars["projectile_var_overrides"]["icon_state"]["value"]
|
||||
var/icon/out_icon = icon(icon, icon_state, frame = 1)
|
||||
projectile_icon_base64 = icon2base64(out_icon)
|
||||
|
||||
if("hand_var_overrides/icon", "hand_var_overrides/icon_state")
|
||||
var/atom/A = /obj/item/melee/touch_attack/sdql
|
||||
var/icon = initial(A.icon)
|
||||
var/icon_state = initial(A.icon_state)
|
||||
if(list_vars["hand_var_overrides"]["icon"])
|
||||
icon = list_vars["hand_var_overrides"]["icon"]["value"]
|
||||
if(list_vars["hand_var_overrides"]["icon_state"])
|
||||
icon_state = list_vars["hand_var_overrides"]["icon_state"]["value"]
|
||||
var/icon/out_icon = icon(icon, icon_state, frame = 1)
|
||||
hand_icon_base64 = icon2base64(out_icon)
|
||||
|
||||
if("overlay", "overlay_icon", "overlay_icon_state")
|
||||
var/icon/out_icon = icon(saved_vars["overlay_icon"], saved_vars["overlay_icon_state"], frame = 1)
|
||||
overlay_icon_base64 = icon2base64(out_icon)
|
||||
|
||||
if("ranged_mousepointer")
|
||||
var/icon/out_icon = icon(saved_vars["ranged_mousepointer"], frame = 1)
|
||||
mouse_icon_base64 = icon2base64(out_icon)
|
||||
|
||||
if("everything")
|
||||
var/icon/out_icon = icon('icons/effects/effects.dmi', "nothing")
|
||||
var/image/out_image = image('icons/mob/actions/backgrounds.dmi', null, saved_vars["action_background_icon_state"])
|
||||
var/overlay_icon = icon(saved_vars["action_icon"], saved_vars["action_icon_state"])
|
||||
out_image.overlays += image(overlay_icon)
|
||||
out_icon.Insert(getFlatIcon(out_image, no_anim = TRUE))
|
||||
action_icon_base64 = icon2base64(out_icon)
|
||||
if(list_vars["projectile_var_overrides"])
|
||||
var/atom/A = /obj/projectile/sdql
|
||||
var/icon = initial(A.icon)
|
||||
var/icon_state = initial(A.icon_state)
|
||||
if(list_vars["projectile_var_overrides"]["icon"])
|
||||
icon = list_vars["projectile_var_overrides"]["icon"]["value"]
|
||||
if(list_vars["projectile_var_overrides"]["icon_state"])
|
||||
icon_state = list_vars["projectile_var_overrides"]["icon_state"]["value"]
|
||||
out_icon = icon(icon, icon_state, frame = 1)
|
||||
projectile_icon_base64 = icon2base64(out_icon)
|
||||
if(list_vars["hand_var_overrides"])
|
||||
var/atom/A = /obj/item/melee/touch_attack/sdql
|
||||
var/icon = initial(A.icon)
|
||||
var/icon_state = initial(A.icon_state)
|
||||
if(list_vars["hand_var_overrides"]["icon"])
|
||||
icon = list_vars["hand_var_overrides"]["icon"]["value"]
|
||||
if(list_vars["hand_var_overrides"]["icon_state"])
|
||||
icon_state = list_vars["hand_var_overrides"]["icon_state"]["value"]
|
||||
out_icon = icon(icon, icon_state, frame = 1)
|
||||
hand_icon_base64 = icon2base64(out_icon)
|
||||
out_icon = icon(saved_vars["overlay_icon"], saved_vars["overlay_icon_state"], frame = 1)
|
||||
overlay_icon_base64 = icon2base64(out_icon)
|
||||
out_icon = icon(saved_vars["ranged_mousepointer"], frame = 1)
|
||||
mouse_icon_base64 = icon2base64(out_icon)
|
||||
|
||||
/datum/give_sdql_spell/proc/toggle_list_var(list_name, list_var)
|
||||
if(list_name in list_vars)
|
||||
if(list_var in list_vars[list_name])
|
||||
list_vars[list_name][list_var]["value"] = !list_vars[list_name][list_var]["value"]
|
||||
|
||||
/datum/give_sdql_spell/proc/set_list_var(list_name, list_var, value)
|
||||
if(list_name in list_vars)
|
||||
if(list_var in list_vars[list_name])
|
||||
list_vars[list_name][list_var]["value"] = value
|
||||
|
||||
/datum/give_sdql_spell/proc/rename_list_var(list_name, list_var, new_name)
|
||||
if(list_var == new_name)
|
||||
return
|
||||
if(list_name in list_vars)
|
||||
var/list/L = list_vars[list_name]
|
||||
var/ind = L.Find(list_var)
|
||||
if(ind)
|
||||
if(new_name in list_vars[list_name])
|
||||
alert = "There is already a variable named [new_name] in [list_name]!"
|
||||
else
|
||||
list_vars[list_name][ind] = new_name
|
||||
|
||||
/datum/give_sdql_spell/proc/change_list_var_type(list_name, list_var, var_type)
|
||||
if(list_name in list_vars)
|
||||
if(list_var in list_vars[list_name])
|
||||
if(list_vars[list_name][list_var]["type"] == "list" && var_type != "list")
|
||||
purge_list_var("[list_name]/[list_var]")
|
||||
list_vars[list_name][list_var]["type"] = var_type
|
||||
switch(var_type)
|
||||
if("string", "path")
|
||||
list_vars[list_name][list_var]["value"] = ""
|
||||
if("bool", "num")
|
||||
list_vars[list_name][list_var]["value"] = 0
|
||||
if("list")
|
||||
list_vars[list_name][list_var]["value"] = null
|
||||
list_vars |= list("[list_name]/[list_var]" = list())
|
||||
|
||||
/datum/give_sdql_spell/proc/remove_list_var(list_name, list_var)
|
||||
if(list_name in list_vars)
|
||||
var/list/L = list_vars[list_name]
|
||||
var/ind = L.Find(list_var)
|
||||
if(ind)
|
||||
if(list_vars[list_name][list_var]["type"] == "list")
|
||||
purge_list_var("[list_name]/[list_var]")
|
||||
L.Cut(ind, ind+1)
|
||||
list_vars[list_name] = L
|
||||
|
||||
/datum/give_sdql_spell/proc/purge_list_var(list_name)
|
||||
var/ind = list_vars.Find(list_name)
|
||||
if(ind)
|
||||
for(var/V in list_vars[list_name])
|
||||
if(list_vars[list_name][V]["type"] == "list")
|
||||
purge_list_var("[list_name]/[V]")
|
||||
list_vars.Cut(ind, ind+1)
|
||||
|
||||
/datum/give_sdql_spell/proc/generate_list_var(list_name)
|
||||
if(!(list_name in list_vars))
|
||||
return null
|
||||
var/list/ret = list()
|
||||
for(var/V in list_vars[list_name])
|
||||
if(list_vars[list_name][V]["type"] == "list")
|
||||
ret[V] = generate_list_var("[list_name]/[V]")
|
||||
else if(list_vars[list_name][V]["type"] == "path")
|
||||
ret[V] = text2path(list_vars[list_name][V]["value"])
|
||||
else if(list_vars[list_name][V]["type"] == "icon")
|
||||
ret[V] = icon(list_vars[list_name][V]["value"])
|
||||
else
|
||||
ret[V] = list_vars[list_name][V]["value"]
|
||||
return ret
|
||||
|
||||
/datum/give_sdql_spell/proc/give_spell()
|
||||
var/path = text2path("/obj/effect/proc_holder/spell/[spell_type]/sdql")
|
||||
var/obj/effect/proc_holder/spell/new_spell = new path
|
||||
for(var/V in saved_vars+list_vars)
|
||||
if(V in new_spell.vars)
|
||||
if(islist(new_spell.vars[V]))
|
||||
var/list/list_var = generate_list_var(V)
|
||||
if(list_var)
|
||||
new_spell.vv_edit_var(V, list_var)
|
||||
else if(isicon(new_spell.vars[V]))
|
||||
new_spell.vv_edit_var(V, icon(saved_vars[V]))
|
||||
else
|
||||
new_spell.vv_edit_var(V, saved_vars[V])
|
||||
|
||||
//delete and recreate the action so the overriden vars are respected by the action button
|
||||
qdel(new_spell.action)
|
||||
new_spell.action = new new_spell.base_action(new_spell)
|
||||
if(target_mob.mind)
|
||||
target_mob.mind.AddSpell(new_spell)
|
||||
else
|
||||
target_mob.AddSpell(new_spell)
|
||||
to_chat(user, span_danger("Spells given to mindless mobs will not be transferred in mindswap or cloning!"))
|
||||
Reference in New Issue
Block a user