TG spawn panel port (#19293)

* TG spawn panel port

* .

* .

* click intercept

* .

* diagonals

* .

* .

* fix path search

* reduce that

* Fixes ghost click

* lol

---------

Co-authored-by: Cameron Lennox <killer65311@gmail.com>
This commit is contained in:
Kashargul
2026-03-29 19:01:27 -04:00
committed by GitHub
co-authored by Cameron Lennox
parent c38c0d0698
commit 7c7aa0d237
38 changed files with 2236 additions and 662 deletions
+3
View File
@@ -130,6 +130,9 @@
#define RANK_SOURCE_BACKUP "rank_backup"
#define RANK_SOURCE_TEMPORARY "rank_temp"
//How many things you can spawn at once with spawn verb/create panel
#define ADMIN_SPAWN_CAP 100
// LOG BROWSE TYPES
#define BROWSE_ROOT_ALL_LOGS 1
#define BROWSE_ROOT_RUNTIME_LOGS 2
@@ -39,7 +39,7 @@
#define COMPONENT_CANCEL_ATTACK_CHAIN (1<<0)
///Skips the specific attack step, continuing for the next one to happen.
#define COMPONENT_SKIP_ATTACK (1<<1)
///from base of atom/attack_ghost(): (mob/dead/observer/ghost)
///from base of atom/attack_ghost(): (mob/observer/dead/ghost)
#define COMSIG_ATOM_ATTACK_GHOST "atom_attack_ghost"
///from base of atom/attack_hand(): (mob/user, list/modifiers)
#define COMSIG_ATOM_ATTACK_HAND "atom_attack_hand"
+1
View File
@@ -37,6 +37,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define IS_BUSY (1<<8) // Atom has a TASK_TARGET_EXCLUSIVE do_after with it as the target.
#define REMOTEVIEW_ON_ENTER (1<<9) // Object starts a remoteview of itself for any mob that enters it with a client. Items will automatically handle their own remoteview, and ignore this.
#define WALL_ITEM (1<<10) // Wall mounted objects
#define ADMIN_SPAWNED (1<<22) // Admin Spawned
#define ATOM_INITIALIZED (1<<23) // Atom has been initialized. Using a flag instead of a variable saves ~25mb total.
//Flags for items (equipment) - Used in /obj/item/var/item_flags
+3
View File
@@ -40,6 +40,9 @@
P.Click(params)
break
if(check_click_intercept(params,A))
return
if(stat)
return
+17 -2
View File
@@ -43,9 +43,11 @@
* mob/RangedAttack(atom,params) - used only ranged, only used for tk and laser eyes but could be changed
*/
/mob/proc/ClickOn(atom/A, params)
if(world.time <= next_click)
if(!checkClickCooldown()) return
setClickCooldown(1) //1/10 of a second, 10 clicks allowed per second.
if(check_click_intercept(params,A) || HAS_TRAIT(src, TRAIT_NO_TRANSFORM))
return
next_click = world.time + 1
if(client && client.buildmode)
build_click(src, client.buildmode, params, A)
@@ -372,3 +374,16 @@
/// MouseWheelOn
/mob/proc/MouseWheelOn(atom/A, delta_x, delta_y, params)
SEND_SIGNAL(src, COMSIG_MOUSE_SCROLL_ON, A, delta_x, delta_y, params)
/mob/proc/check_click_intercept(params,A)
//Client level intercept
if(client?.click_intercept)
if(call(client.click_intercept, "InterceptClickOn")(src, params, A))
return TRUE
//Mob level intercept
if(click_intercept)
if(call(click_intercept, "InterceptClickOn")(src, params, A))
return TRUE
return FALSE
+3
View File
@@ -10,6 +10,9 @@
if(!checkClickCooldown())
return
if(check_click_intercept(params,A))
return
setClickCooldown(1)
if(client.buildmode) // comes after object.Click to allow buildmode gui objects to be clicked
@@ -33,7 +33,7 @@
/atom/movable/screen/movable/action_button/proc/can_use(mob/user)
// if(isobserver(user))
// var/mob/dead/observer/dead_mob = user
// var/mob/observer/dead/dead_mob = user
// if(dead_mob.observetarget) // Observers can only click on action buttons if they're not observing something
// return FALSE
@@ -327,7 +327,7 @@ GLOBAL_LIST_INIT(palette_removed_matrix, list(1.4,0,0,0, 0.7,0.4,0,0, 0.4,0,0.6,
/atom/movable/screen/button_palette/proc/can_use(mob/user)
if(isobserver(user))
// var/mob/dead/observer/O = user
// var/mob/observer/dead/O = user
// return !O.observetarget
return TRUE
return TRUE
@@ -388,7 +388,7 @@ GLOBAL_LIST_INIT(palette_removed_matrix, list(1.4,0,0,0, 0.7,0.4,0,0, 0.4,0,0.6,
/atom/movable/screen/palette_scroll/proc/can_use(mob/user)
if(isobserver(user))
// var/mob/dead/observer/O = user
// var/mob/observer/dead/O = user
// return !O.observetarget
return TRUE
return TRUE
+1 -1
View File
@@ -414,7 +414,7 @@ so as to remain in compliance with the most up-to-date laws."
// /atom/movable/screen/alert/notify_jump/Click()
// if(!usr || !usr.client) return
// if(!jump_target) return
// var/mob/dead/observer/G = usr
// var/mob/observer/dead/G = usr
// if(!istype(G)) return
// if(attack_not_jump)
// jump_target.attack_ghost(G)
+2 -2
View File
@@ -192,7 +192,7 @@
/* I wish we had this. Not yet, though.
/datum/hud/ghost/show_hud(version = 0, mob/viewmob)
// don't show this HUD if observing; show the HUD of the observee
var/mob/dead/observer/O = mymob
var/mob/observer/dead/O = mymob
if (istype(O) && O.observetarget)
plane_masters_update()
return FALSE
@@ -208,7 +208,7 @@
//We should only see observed mob alerts.
/datum/hud/ghost/reorganize_alerts(mob/viewmob)
var/mob/dead/observer/O = mymob
var/mob/observer/dead/O = mymob
if (istype(O) && O.observetarget)
return
. = ..()
+41 -10
View File
@@ -11,6 +11,8 @@
to_chat(src, span_notice("You will no longer examine things you click on."))
/mob/observer/dead/DblClickOn(var/atom/A, var/params)
if(check_click_intercept(params,A))
return
if(client.buildmode)
build_click(src, client.buildmode, params, A)
return
@@ -28,21 +30,50 @@
stop_following()
forceMove(get_turf(A))
/mob/observer/dead/ClickOn(var/atom/A, var/params)
if(client.buildmode)
/mob/observer/dead/ClickOn(atom/A, params)
if(!checkClickCooldown()) return
setClickCooldown(1)
if(check_click_intercept(params,A))
return
if(client && client.buildmode)
build_click(src, client.buildmode, params, A)
return
if(!checkClickCooldown()) return
setClickCooldown(4)
var/list/modifiers = params2list(params)
if(modifiers["shift"])
examinate(A)
if(LAZYACCESS(modifiers, BUTTON4) || LAZYACCESS(modifiers, BUTTON5))
return
if(modifiers["alt"]) // alt and alt-gr (rightalt)
var/turf/T = get_turf(A)
if(T && TurfAdjacent(T))
T.click_alt(src)
if(LAZYACCESS(modifiers, SHIFT_CLICK))
if(LAZYACCESS(modifiers, MIDDLE_CLICK))
ShiftMiddleClickOn(A)
return
if(LAZYACCESS(modifiers, CTRL_CLICK))
CtrlShiftClickOn(A)
return
if (LAZYACCESS(modifiers, ALT_CLICK))
alt_shift_click_on(A)
return
ShiftClickOn(A) //Should in most cases call examinate() unless we block things from examining us.
return
if(LAZYACCESS(modifiers, MIDDLE_CLICK))
if(LAZYACCESS(modifiers, CTRL_CLICK))
CtrlMiddleClickOn(A)
else
MiddleClickOn(A, params)
return
if(LAZYACCESS(modifiers, ALT_CLICK)) // alt and alt-gr (rightalt)
if(LAZYACCESS(modifiers, RIGHT_CLICK))
AltClickSecondaryOn(A)
else
AltClickOn(A)
return
if(LAZYACCESS(modifiers, CTRL_CLICK))
CtrlClickOn(A)
return
// You are responsible for checking config.ghost_interaction when you override this function
// Not all of them require checking, see below
A.attack_ghost(src)
+1 -1
View File
@@ -125,6 +125,6 @@
if(orbiters)
for(var/datum/orbit/O as anything in orbiters)
if(O.orbiter && isobserver(O.orbiter))
var/mob/dead/observer/D = O.orbiter
var/mob/observer/dead/D = O.orbiter
D.ManualFollow(target)
*/
+1 -1
View File
@@ -18,7 +18,7 @@
if(A)
A.forceMove(src) // helo
podfall(auto_open)
air = new
air = new
/obj/structure/drop_pod/Destroy()
. = ..()
+200 -203
View File
@@ -49,12 +49,12 @@ ADMIN_VERB_ONLY_CONTEXT_MENU(show_player_panel, R_HOLDER, "Show Player Panel", m
var/body = "Options panel for " + span_bold("[player]")
if(player.client)
body += " played by " + span_bold("[player.client]")
body += "\[<A href='byond://?_src_=holder;[HrefToken()];editrights=[(GLOB.admin_datums[player.client.ckey] || GLOB.deadmins[player.client.ckey]) ? "rank" : "add"];key=[player.key]'>[player.client.holder ? player.client.holder.rank_names() : "Player"]</A>\]"
body += "\[<a href='byond://?_src_=holder;[HrefToken()];editrights=[(GLOB.admin_datums[player.client.ckey] || GLOB.deadmins[player.client.ckey]) ? "rank" : "add"];key=[player.key]'>[player.client.holder ? player.client.holder.rank_names() : "Player"]</a>\]"
if(isnewplayer(player))
body += span_bold("Hasn't Entered Game")
else
body += " \[<A href='byond://?_src_=holder;[HrefToken()];revive=[REF(player)]'>Heal</A>\] "
body += " \[<a href='byond://?_src_=holder;[HrefToken()];revive=[REF(player)]'>Heal</a>\] "
if(player.client)
body += "<br>" + span_bold("First connection:") + "[player.client.player_age] days ago"
@@ -63,42 +63,42 @@ ADMIN_VERB_ONLY_CONTEXT_MENU(show_player_panel, R_HOLDER, "Show Player Panel", m
body += {"
<br><br>\[
<a href='byond://?_src_=vars;[HrefToken()];Vars=\ref[player]'>VV</a> -
<a href='byond://?_src_=holder;[HrefToken()];traitor=\ref[player]'>TP</a> -
<a href='byond://??_src_=holder;[HrefToken()];priv_msg=\ref[player]'>PM</a> -
<a href='byond://?_src_=holder;[HrefToken()];subtlemessage=\ref[player]'>SM</a> -
<a href='byond://?_src_=vars;[HrefToken()];Vars=[REF(player)]'>VV</a> -
<a href='byond://?_src_=holder;[HrefToken()];traitor=[REF(player)]'>TP</a> -
<a href='byond://??_src_=holder;[HrefToken()];priv_msg=[REF(player)]'>PM</a> -
<a href='byond://?_src_=holder;[HrefToken()];subtlemessage=[REF(player)]'>SM</a> -
[admin_jump_link(player, src)]\] <br>
"} + span_bold("Mob type:") + {"[player.type]<br>
"} + span_bold("Inactivity time:") + {" [player.client ? "[player.client.inactivity/600] minutes" : "Logged out"]<br/><br/>
<A href='byond://?_src_=holder;[HrefToken()];boot2=\ref[player]'>Kick</A> |
<A href='byond://?_src_=holder;[HrefToken()];warn=[player.ckey]'>Warn</A> |
<A href='byond://?_src_=holder;[HrefToken()];newban=\ref[player]'>Ban</A> |
<A href='byond://?_src_=holder;[HrefToken()];jobban2=\ref[player]'>Jobban</A> |
<A href='byond://?_src_=holder;[HrefToken()];notes=show;mob=\ref[player]'>Notes</A>
<a href='byond://?_src_=holder;[HrefToken()];boot2=[REF(player)]'>Kick</a> |
<a href='byond://?_src_=holder;[HrefToken()];warn=[player.ckey]'>Warn</a> |
<a href='byond://?_src_=holder;[HrefToken()];newban=[REF(player)]'>Ban</a> |
<a href='byond://?_src_=holder;[HrefToken()];jobban2=[REF(player)]'>Jobban</a> |
<a href='byond://?_src_=holder;[HrefToken()];notes=show;mob=[REF(player)]'>Notes</a>
"}
if(player.client)
body += "| <A href='byond://?_src_=holder;[HrefToken()];sendtoprison=\ref[player]'>Prison</A> | "
body += "\ <A href='byond://?_src_=holder;[HrefToken()];sendbacktolobby=\ref[player]'>Send back to Lobby</A> | "
body += "| <a href='byond://?_src_=holder;[HrefToken()];sendtoprison=[REF(player)]'>Prison</a> | "
body += "\ <a href='byond://?_src_=holder;[HrefToken()];sendbacktolobby=[REF(player)]'>Send back to Lobby</a> | "
var/muted = player.client.prefs.muted
body += {"<br>"} + span_bold("Mute: ") + {"
\[<A href='byond://?_src_=holder;[HrefToken()];mute=\ref[player];mute_type=[MUTE_IC]'>[(muted & MUTE_IC) ? span_red("IC") : span_blue("IC")]</a> |
<A href='byond://?_src_=holder;[HrefToken()];mute=\ref[player];mute_type=[MUTE_OOC]'>[(muted & MUTE_OOC) ? span_red("OOC") : span_blue("OOC")]</a> |
<A href='byond://?_src_=holder;[HrefToken()];mute=\ref[player];mute_type=[MUTE_LOOC]'>[(muted & MUTE_LOOC) ? span_red("LOOC") : span_blue("LOOC")]</a> |
<A href='byond://?_src_=holder;[HrefToken()];mute=\ref[player];mute_type=[MUTE_PRAY]'>[(muted & MUTE_PRAY) ? span_red("PRAY") : span_blue("PRAY")]</a> |
<A href='byond://?_src_=holder;[HrefToken()];mute=\ref[player];mute_type=[MUTE_ADMINHELP]'>[(muted & MUTE_ADMINHELP) ? span_red("ADMINHELP") : span_blue("ADMINHELP")]</a> |
<A href='byond://?_src_=holder;[HrefToken()];mute=\ref[player];mute_type=[MUTE_DEADCHAT]'>[(muted & MUTE_DEADCHAT) ? span_red("DEADCHAT") : span_blue("DEADCHAT")]</a>\]
(<A href='byond://?_src_=holder;[HrefToken()];mute=\ref[player];mute_type=[MUTE_ALL]'>[(muted & MUTE_ALL) ? span_red("toggle all") : span_blue("toggle all")]</a>)
\[<a href='byond://?_src_=holder;[HrefToken()];mute=[REF(player)];mute_type=[MUTE_IC]'>[(muted & MUTE_IC) ? span_red("IC") : span_blue("IC")]</a> |
<a href='byond://?_src_=holder;[HrefToken()];mute=[REF(player)];mute_type=[MUTE_OOC]'>[(muted & MUTE_OOC) ? span_red("OOC") : span_blue("OOC")]</a> |
<a href='byond://?_src_=holder;[HrefToken()];mute=[REF(player)];mute_type=[MUTE_LOOC]'>[(muted & MUTE_LOOC) ? span_red("LOOC") : span_blue("LOOC")]</a> |
<a href='byond://?_src_=holder;[HrefToken()];mute=[REF(player)];mute_type=[MUTE_PRAY]'>[(muted & MUTE_PRAY) ? span_red("PRAY") : span_blue("PRAY")]</a> |
<a href='byond://?_src_=holder;[HrefToken()];mute=[REF(player)];mute_type=[MUTE_ADMINHELP]'>[(muted & MUTE_ADMINHELP) ? span_red("ADMINHELP") : span_blue("ADMINHELP")]</a> |
<a href='byond://?_src_=holder;[HrefToken()];mute=[REF(player)];mute_type=[MUTE_DEADCHAT]'>[(muted & MUTE_DEADCHAT) ? span_red("DEADCHAT") : span_blue("DEADCHAT")]</a>\]
(<a href='byond://?_src_=holder;[HrefToken()];mute=[REF(player)];mute_type=[MUTE_ALL]'>[(muted & MUTE_ALL) ? span_red("toggle all") : span_blue("toggle all")]</a>)
"}
body += {"<br><br>
<A href='byond://?_src_=holder;[HrefToken()];jumpto=\ref[player]'>"} + span_bold("Jump to") + {"</A> |
<A href='byond://?_src_=holder;[HrefToken()];getmob=\ref[player]'>Get</A> |
<A href='byond://?_src_=holder;[HrefToken()];sendmob=\ref[player]'>Send To</A>
<a href='byond://?_src_=holder;[HrefToken()];jumpto=[REF(player)]'>"} + span_bold("Jump to") + {"</a> |
<a href='byond://?_src_=holder;[HrefToken()];getmob=[REF(player)]'>Get</a> |
<a href='byond://?_src_=holder;[HrefToken()];sendmob=[REF(player)]'>Send To</a>
<br><br>
[check_rights(R_ADMIN|R_MOD|R_EVENT,0) ? "<A href='byond://?_src_=holder;[HrefToken()];traitor=\ref[player]'>Traitor panel</A> | " : "" ]
<A href='byond://?_src_=holder;[HrefToken()];narrateto=\ref[player]'>Narrate to</A> |
<A href='byond://?_src_=holder;[HrefToken()];subtlemessage=\ref[player]'>Subtle message</A>
[check_rights(R_ADMIN|R_MOD|R_EVENT,0) ? "<a href='byond://?_src_=holder;[HrefToken()];traitor=[REF(player)]'>Traitor panel</a> | " : "" ]
<a href='byond://?_src_=holder;[HrefToken()];narrateto=[REF(player)]'>Narrate to</a> |
<a href='byond://?_src_=holder;[HrefToken()];subtlemessage=[REF(player)]'>Subtle message</a>
"}
if (player.client)
@@ -111,30 +111,30 @@ ADMIN_VERB_ONLY_CONTEXT_MENU(show_player_panel, R_HOLDER, "Show Player Panel", m
if(issmall(player))
body += span_bold("Monkeyized") + " | "
else
body += "<A href='byond://?_src_=holder;[HrefToken()];turn_monkey=\ref[player]'>Monkeyize</A> | "
body += "<a href='byond://?_src_=holder;[HrefToken()];turn_monkey=[REF(player)]'>Monkeyize</a> | "
//Corgi
if(iscorgi(player))
body += span_bold("Corgized") + " | "
else
body += "<A href='byond://?_src_=holder;[HrefToken()];corgione=\ref[player]'>Corgize</A> | "
body += "<a href='byond://?_src_=holder;[HrefToken()];corgione=[REF(player)]'>Corgize</a> | "
//AI / Cyborg
if(isAI(player))
body += span_bold("Is an AI ")
else if(ishuman(player))
body += {"<A href='byond://?_src_=holder;[HrefToken()];turn_ai=\ref[player]'>Make AI</A> |
<A href='byond://?_src_=holder;[HrefToken()];turn_robot=\ref[player]'>Make Robot</A> |
<A href='byond://?_src_=holder;[HrefToken()];turn_alien=\ref[player]'>Make Alien</A>
body += {"<a href='byond://?_src_=holder;[HrefToken()];turn_ai=[REF(player)]'>Make AI</a> |
<a href='byond://?_src_=holder;[HrefToken()];turn_robot=[REF(player)]'>Make Robot</a> |
<a href='byond://?_src_=holder;[HrefToken()];turn_alien=[REF(player)]'>Make Alien</a>
"}
//Simple Animals
if(isanimal(player))
body += "<A href='byond://?_src_=holder;[HrefToken()];makeanimal=\ref[player]'>Re-Animalize</A> | "
body += "<a href='byond://?_src_=holder;[HrefToken()];makeanimal=[REF(player)]'>Re-Animalize</a> | "
else
body += "<A href='byond://?_src_=holder;[HrefToken()];makeanimal=\ref[player]'>Animalize</A> | "
body += "<a href='byond://?_src_=holder;[HrefToken()];makeanimal=[REF(player)]'>Animalize</a> | "
body += "<A href='byond://?_src_=holder;[HrefToken()];respawn=\ref[player.client]'>Respawn</A> | "
body += "<a href='byond://?_src_=holder;[HrefToken()];respawn=[REF(player.client)]'>Respawn</a> | "
// DNA2 - Admin Hax
if(player.dna && iscarbon(player))
@@ -170,7 +170,7 @@ ADMIN_VERB_ONLY_CONTEXT_MENU(show_player_panel, R_HOLDER, "Show Player Panel", m
bname = span_orange(bname)
else
bname = span_red(bname)
body += "<A href='byond://?_src_=holder;[HrefToken()];togmutate=\ref[player];block=[block]' title='[tname]'>[bname]</A><sub>[block]</sub>" // Traitgenes edit - show trait linked names on mouseover
body += "<a href='byond://?_src_=holder;[HrefToken()];togmutate=[REF(player)];block=[block]' title='[tname]'>[bname]</a><sub>[block]</sub>" // Traitgenes edit - show trait linked names on mouseover
else
body += "[block]"
body+="</td>"
@@ -178,45 +178,45 @@ ADMIN_VERB_ONLY_CONTEXT_MENU(show_player_panel, R_HOLDER, "Show Player Panel", m
body += {"<br><br>
"} + span_bold("Rudimentary transformation:") + span_normal("<br>These transformations only create a new mob type and copy stuff over. They do not take into account MMIs and similar mob-specific things. The buttons in 'Transformations' are preferred, when possible.") + {"<br>
<A href='byond://?_src_=holder;[HrefToken()];simplemake=observer;mob=\ref[player]'>Observer</A> |
\[ Xenos: <A href='byond://?_src_=holder;[HrefToken()];simplemake=larva;mob=\ref[player]'>Larva</A>
<A href='byond://?_src_=holder;[HrefToken()];simplemake=human;species=Xenomorph Drone;mob=\ref[player]'>Drone</A>
<A href='byond://?_src_=holder;[HrefToken()];simplemake=human;species=Xenomorph Hunter;mob=\ref[player]'>Hunter</A>
<A href='byond://?_src_=holder;[HrefToken()];simplemake=human;species=Xenomorph Sentinel;mob=\ref[player]'>Sentinel</A>
<A href='byond://?_src_=holder;[HrefToken()];simplemake=human;species=Xenomorph Queen;mob=\ref[player]'>Queen</A> \] |
\[ Crew: <A href='byond://?_src_=holder;[HrefToken()];simplemake=human;mob=\ref[player]'>Human</A>
<A href='byond://?_src_=holder;[HrefToken()];simplemake=human;species=Unathi;mob=\ref[player]'>Unathi</A>
<A href='byond://?_src_=holder;[HrefToken()];simplemake=human;species=Tajaran;mob=\ref[player]'>Tajaran</A>
<A href='byond://?_src_=holder;[HrefToken()];simplemake=human;species=Skrell;mob=\ref[player]'>Skrell</A> \] | \[
<A href='byond://?_src_=holder;[HrefToken()];simplemake=nymph;mob=\ref[player]'>Nymph</A>
<A href='byond://?_src_=holder;[HrefToken()];simplemake=human;species='Diona';mob=\ref[player]'>Diona</A> \] |
\[ slime: <A href='byond://?_src_=holder;[HrefToken()];simplemake=slime;mob=\ref[player]'>Baby</A>,
<A href='byond://?_src_=holder;[HrefToken()];simplemake=adultslime;mob=\ref[player]'>Adult</A> \]
<A href='byond://?_src_=holder;[HrefToken()];simplemake=monkey;mob=\ref[player]'>Monkey</A> |
<A href='byond://?_src_=holder;[HrefToken()];simplemake=robot;mob=\ref[player]'>Cyborg</A> |
<A href='byond://?_src_=holder;[HrefToken()];simplemake=cat;mob=\ref[player]'>Cat</A> |
<A href='byond://?_src_=holder;[HrefToken()];simplemake=runtime;mob=\ref[player]'>Runtime</A> |
<A href='byond://?_src_=holder;[HrefToken()];simplemake=corgi;mob=\ref[player]'>Corgi</A> |
<A href='byond://?_src_=holder;[HrefToken()];simplemake=ian;mob=\ref[player]'>Ian</A> |
<A href='byond://?_src_=holder;[HrefToken()];simplemake=crab;mob=\ref[player]'>Crab</A> |
<A href='byond://?_src_=holder;[HrefToken()];simplemake=coffee;mob=\ref[player]'>Coffee</A> |
\[ Construct: <A href='byond://?_src_=holder;[HrefToken()];simplemake=constructarmoured;mob=\ref[player]'>Armoured</A> ,
<A href='byond://?_src_=holder;[HrefToken()];simplemake=constructbuilder;mob=\ref[player]'>Builder</A> ,
<A href='byond://?_src_=holder;[HrefToken()];simplemake=constructwraith;mob=\ref[player]'>Wraith</A> \]
<A href='byond://?_src_=holder;[HrefToken()];simplemake=shade;mob=\ref[player]'>Shade</A>
<a href='byond://?_src_=holder;[HrefToken()];simplemake=observer;mob=[REF(player)]'>Observer</a> |
\[ Xenos: <a href='byond://?_src_=holder;[HrefToken()];simplemake=larva;mob=[REF(player)]'>Larva</a>
<a href='byond://?_src_=holder;[HrefToken()];simplemake=human;species=Xenomorph Drone;mob=[REF(player)]'>Drone</a>
<a href='byond://?_src_=holder;[HrefToken()];simplemake=human;species=Xenomorph Hunter;mob=[REF(player)]'>Hunter</a>
<a href='byond://?_src_=holder;[HrefToken()];simplemake=human;species=Xenomorph Sentinel;mob=[REF(player)]'>Sentinel</a>
<a href='byond://?_src_=holder;[HrefToken()];simplemake=human;species=Xenomorph Queen;mob=[REF(player)]'>Queen</a> \] |
\[ Crew: <a href='byond://?_src_=holder;[HrefToken()];simplemake=human;mob=[REF(player)]'>Human</a>
<a href='byond://?_src_=holder;[HrefToken()];simplemake=human;species=Unathi;mob=[REF(player)]'>Unathi</a>
<a href='byond://?_src_=holder;[HrefToken()];simplemake=human;species=Tajaran;mob=[REF(player)]'>Tajaran</a>
<a href='byond://?_src_=holder;[HrefToken()];simplemake=human;species=Skrell;mob=[REF(player)]'>Skrell</a> \] | \[
<a href='byond://?_src_=holder;[HrefToken()];simplemake=nymph;mob=[REF(player)]'>Nymph</a>
<a href='byond://?_src_=holder;[HrefToken()];simplemake=human;species='Diona';mob=[REF(player)]'>Diona</a> \] |
\[ slime: <a href='byond://?_src_=holder;[HrefToken()];simplemake=slime;mob=[REF(player)]'>Baby</a>,
<a href='byond://?_src_=holder;[HrefToken()];simplemake=adultslime;mob=[REF(player)]'>Adult</a> \]
<a href='byond://?_src_=holder;[HrefToken()];simplemake=monkey;mob=[REF(player)]'>Monkey</a> |
<a href='byond://?_src_=holder;[HrefToken()];simplemake=robot;mob=[REF(player)]'>Cyborg</a> |
<a href='byond://?_src_=holder;[HrefToken()];simplemake=cat;mob=[REF(player)]'>Cat</a> |
<a href='byond://?_src_=holder;[HrefToken()];simplemake=runtime;mob=[REF(player)]'>Runtime</a> |
<a href='byond://?_src_=holder;[HrefToken()];simplemake=corgi;mob=[REF(player)]'>Corgi</a> |
<a href='byond://?_src_=holder;[HrefToken()];simplemake=ian;mob=[REF(player)]'>Ian</a> |
<a href='byond://?_src_=holder;[HrefToken()];simplemake=crab;mob=[REF(player)]'>Crab</a> |
<a href='byond://?_src_=holder;[HrefToken()];simplemake=coffee;mob=[REF(player)]'>Coffee</a> |
\[ Construct: <a href='byond://?_src_=holder;[HrefToken()];simplemake=constructarmoured;mob=[REF(player)]'>Armoured</a> ,
<a href='byond://?_src_=holder;[HrefToken()];simplemake=constructbuilder;mob=[REF(player)]'>Builder</a> ,
<a href='byond://?_src_=holder;[HrefToken()];simplemake=constructwraith;mob=[REF(player)]'>Wraith</a> \]
<a href='byond://?_src_=holder;[HrefToken()];simplemake=shade;mob=[REF(player)]'>Shade</a>
<br>
"}
body += {"<br><br>
"} + span_bold("Other actions:") + {"
<br>
<A href='byond://?_src_=holder;[HrefToken()];forcespeech=\ref[player]'>Forcesay</A>
<a href='byond://?_src_=holder;[HrefToken()];forcespeech=[REF(player)]'>Forcesay</a>
"}
if (player.client)
body += {" |
<A href='byond://?_src_=holder;[HrefToken()];tdome1=\ref[player]'>Thunderdome 1</A> |
<A href='byond://?_src_=holder;[HrefToken()];tdome2=\ref[player]'>Thunderdome 2</A> |
<A href='byond://?_src_=holder;[HrefToken()];tdomeadmin=\ref[player]'>Thunderdome Admin</A> |
<A href='byond://?_src_=holder;[HrefToken()];tdomeobserve=\ref[player]'>Thunderdome Observer</A> |
<a href='byond://?_src_=holder;[HrefToken()];tdome1=[REF(player)]'>Thunderdome 1</a> |
<a href='byond://?_src_=holder;[HrefToken()];tdome2=[REF(player)]'>Thunderdome 2</a> |
<a href='byond://?_src_=holder;[HrefToken()];tdomeadmin=[REF(player)]'>Thunderdome Admin</a> |
<a href='byond://?_src_=holder;[HrefToken()];tdomeobserve=[REF(player)]'>Thunderdome Observer</a> |
"}
// language toggles
body += "<br><br>" + span_bold("Languages:") + "<br>"
@@ -227,9 +227,9 @@ ADMIN_VERB_ONLY_CONTEXT_MENU(show_player_panel, R_HOLDER, "Show Player Panel", m
if(!f) body += " | "
else f = 0
if(L in player.languages)
body += "<a href='byond://?_src_=holder;[HrefToken()];toglang=\ref[player];lang=[html_encode(k)]'>[span_green(k)]</a>"
body += "<a href='byond://?_src_=holder;[HrefToken()];toglang=[REF(player)];lang=[html_encode(k)]'>[span_green(k)]</a>"
else
body += "<a href='byond://?_src_=holder;[HrefToken()];toglang=\ref[player];lang=[html_encode(k)]'>[span_red(k)]</a>"
body += "<a href='byond://?_src_=holder;[HrefToken()];toglang=[REF(player)];lang=[html_encode(k)]'>[span_red(k)]</a>"
body += {"<br>"}
@@ -279,28 +279,28 @@ ADMIN_VERB(access_news_network, R_ADMIN|R_EVENT, "Access Newscaster Network", "A
var/datum/admins/admin_holder = user.holder
switch(admin_holder.admincaster_screen)
if(0)
dat += {"Welcome to the admin newscaster.<BR> Here you can add, edit and censor every newspiece on the network.
<BR>Feed channels and stories entered through here will be uneditable and handled as official news by the rest of the units.
<BR>Note that this panel allows full freedom over the news network, there are no constrictions except the few basic ones. Don't break things!
dat += {"Welcome to the admin newscaster.<br> Here you can add, edit and censor every newspiece on the network.
<br>Feed channels and stories entered through here will be uneditable and handled as official news by the rest of the units.
<br>Note that this panel allows full freedom over the news network, there are no constrictions except the few basic ones. Don't break things!
"}
if(GLOB.news_network.wanted_issue)
dat+= "<HR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_view_wanted=1'>Read Wanted Issue</A>"
dat+= "<HR><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_view_wanted=1'>Read Wanted Issue</a>"
dat+= {"<HR><BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_create_channel=1'>Create Feed Channel</A>
<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_view=1'>View Feed Channels</A>
<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_create_feed_story=1'>Submit new Feed story</A>
<BR><BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];mach_close=newscaster_main'>Exit</A>
dat+= {"<HR><br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_create_channel=1'>Create Feed Channel</a>
<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_view=1'>View Feed Channels</a>
<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_create_feed_story=1'>Submit new Feed story</a>
<br><br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];mach_close=newscaster_main'>Exit</a>
"}
var/wanted_already = 0
if(GLOB.news_network.wanted_issue)
wanted_already = 1
dat+={"<HR>"} + span_bold("Feed Security functions:") + {"<BR>
<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_menu_wanted=1'>[(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue</A>
<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_menu_censor_story=1'>Censor Feed Stories</A>
<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_menu_censor_channel=1'>Mark Feed Channel with [using_map.company_name] D-Notice (disables and locks the channel.</A>
<BR><HR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_set_signature=1'>The newscaster recognises you as:<BR>"} + span_green("[admin_holder.admincaster_signature]") + {"</A>
dat+={"<HR>"} + span_bold("Feed Security functions:") + {"<br>
<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_menu_wanted=1'>[(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue</a>
<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_menu_censor_story=1'>Censor Feed Stories</a>
<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_menu_censor_channel=1'>Mark Feed Channel with [using_map.company_name] D-Notice (disables and locks the channel.</a>
<br><HR><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_set_signature=1'>The newscaster recognises you as:<br>"} + span_green("[admin_holder.admincaster_signature]") + {"</a>
"}
if(1)
dat+= "Station Feed Channels<HR>"
@@ -309,97 +309,97 @@ ADMIN_VERB(access_news_network, R_ADMIN|R_EVENT, "Access Newscaster Network", "A
else
for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels)
if(CHANNEL.is_admin_channel)
dat+=span_bold("<FONT style='BACKGROUND-COLOR: LightGreen'><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_show_channel=\ref[CHANNEL]'>[CHANNEL.channel_name]</A></FONT>") + "<BR>"
dat+=span_bold("<FONT style='BACKGROUND-COLOR: LightGreen'><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_show_channel=[REF(CHANNEL)]'>[CHANNEL.channel_name]</a></FONT>") + "<br>"
else
dat+=span_bold("<A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_show_channel=\ref[CHANNEL]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? (span_red("***")) : null]<BR>")
dat+={"<BR><HR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_refresh=1'>Refresh</A>
<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[0]'>Back</A>
dat+=span_bold("<a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_show_channel=[REF(CHANNEL)]'>[CHANNEL.channel_name]</a> [(CHANNEL.censored) ? (span_red("***")) : null]<br>")
dat+={"<br><HR><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_refresh=1'>Refresh</a>
<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[0]'>Back</a>
"}
if(2)
dat+={"
Creating new Feed Channel...
<HR>"} + span_bold("<A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_set_channel_name=1'>Channel Name</A>:") + {" [admin_holder.admincaster_feed_channel.channel_name]<BR>
"} + span_bold("<A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_set_signature=1'>Channel Author</A>:") + {" "} + span_green("[admin_holder.admincaster_signature]") + {"<BR>
"} + span_bold("<A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_set_channel_lock=1'>Will Accept Public Feeds</A>:") + {" [(admin_holder.admincaster_feed_channel.locked) ? ("NO") : ("YES")]<BR><BR>
<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_submit_new_channel=1'>Submit</A><BR><BR><A href='byond://?src=\ref[src];[HrefToken()];ac_setScreen=[0]'>Cancel</A><BR>
<HR>"} + span_bold("<a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_set_channel_name=1'>Channel Name</a>:") + {" [admin_holder.admincaster_feed_channel.channel_name]<br>
"} + span_bold("<a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_set_signature=1'>Channel Author</a>:") + {" "} + span_green("[admin_holder.admincaster_signature]") + {"<br>
"} + span_bold("<a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_set_channel_lock=1'>Will Accept Public Feeds</a>:") + {" [(admin_holder.admincaster_feed_channel.locked) ? ("NO") : ("YES")]<br><br>
<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_submit_new_channel=1'>Submit</a><br><br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[0]'>Cancel</a><br>
"}
if(3)
dat+={"
Creating new Feed Message...
<HR>"} + span_bold("<A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_set_channel_receiving=1'>Receiving Channel</A>:") + {" [admin_holder.admincaster_feed_channel.channel_name]<BR>
"} + span_bold("Message Author:") + {" "} + span_green("[admin_holder.admincaster_signature]") + {"<BR>
"} + span_bold("<A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_set_new_message=1'>Message Body</A>:") + {" [admin_holder.admincaster_feed_message.body] <BR>
<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_submit_new_message=1'>Submit</A><BR><BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[0]'>Cancel</A><BR>
<HR>"} + span_bold("<a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_set_channel_receiving=1'>Receiving Channel</a>:") + {" [admin_holder.admincaster_feed_channel.channel_name]<br>
"} + span_bold("Message Author:") + {" "} + span_green("[admin_holder.admincaster_signature]") + {"<br>
"} + span_bold("<a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_set_new_message=1'>Message Body</a>:") + {" [admin_holder.admincaster_feed_message.body] <br>
<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_submit_new_message=1'>Submit</a><br><br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[0]'>Cancel</a><br>
"}
if(4)
dat+={"
Feed story successfully submitted to [admin_holder.admincaster_feed_channel.channel_name].<BR><BR>
<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[0]'>Return</A><BR>
Feed story successfully submitted to [admin_holder.admincaster_feed_channel.channel_name].<br><br>
<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[0]'>Return</a><br>
"}
if(5)
dat+={"
Feed Channel [admin_holder.admincaster_feed_channel.channel_name] created successfully.<BR><BR>
<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[0]'>Return</A><BR>
Feed Channel [admin_holder.admincaster_feed_channel.channel_name] created successfully.<br><br>
<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[0]'>Return</a><br>
"}
if(6)
dat+=span_bold(span_maroon("ERROR: Could not submit Feed story to Network.")) + "<HR><BR>"
dat+=span_bold(span_maroon("ERROR: Could not submit Feed story to Network.")) + "<HR><br>"
if(admin_holder.admincaster_feed_channel.channel_name=="")
dat+=span_maroon("•Invalid receiving channel name.") + "<BR>"
dat+=span_maroon("•Invalid receiving channel name.") + "<br>"
if(admin_holder.admincaster_feed_message.body == "" || admin_holder.admincaster_feed_message.body == "\[REDACTED\]" || admin_holder.admincaster_feed_message.title == "")
dat+=span_maroon("•Invalid message body.") + "<BR>"
dat+="<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[3]'>Return</A><BR>"
dat+=span_maroon("•Invalid message body.") + "<br>"
dat+="<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[3]'>Return</a><br>"
if(7)
dat+=span_bold(span_maroon("ERROR: Could not submit Feed Channel to Network.")) + "<HR><BR>"
dat+=span_bold(span_maroon("ERROR: Could not submit Feed Channel to Network.")) + "<HR><br>"
if(admin_holder.admincaster_feed_channel.channel_name =="" || admin_holder.admincaster_feed_channel.channel_name == "\[REDACTED\]")
dat+=span_maroon("•Invalid channel name.") + "<BR>"
dat+=span_maroon("•Invalid channel name.") + "<br>"
var/check = 0
for(var/datum/feed_channel/FC in GLOB.news_network.network_channels)
if(FC.channel_name == admin_holder.admincaster_feed_channel.channel_name)
check = 1
break
if(check)
dat+=span_maroon("•Channel name already in use.") + "<BR>"
dat+="<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[2]'>Return</A><BR>"
dat+=span_maroon("•Channel name already in use.") + "<br>"
dat+="<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[2]'>Return</a><br>"
if(9)
dat+=span_bold("[admin_holder.admincaster_feed_channel.channel_name]: ") + span_small("\[created by: [span_maroon("[admin_holder.admincaster_feed_channel.author]")]\]") + "<HR>"
if(admin_holder.admincaster_feed_channel.censored)
dat+={"
"} + span_red(span_bold("ATTENTION: ")) + {"This channel has been deemed as threatening to the welfare of the station, and marked with a [using_map.company_name] D-Notice.<BR>
No further feed story additions are allowed while the D-Notice is in effect.<BR><BR>
"} + span_red(span_bold("ATTENTION: ")) + {"This channel has been deemed as threatening to the welfare of the station, and marked with a [using_map.company_name] D-Notice.<br>
No further feed story additions are allowed while the D-Notice is in effect.<br><br>
"}
else
if( isemptylist(admin_holder.admincaster_feed_channel.messages) )
dat+=span_italics("No feed messages found in channel...") + "<BR>"
dat+=span_italics("No feed messages found in channel...") + "<br>"
else
var/i = 0
for(var/datum/feed_message/MESSAGE in admin_holder.admincaster_feed_channel.messages)
i++
//dat+="-[MESSAGE.body] <BR>"
//dat+="-[MESSAGE.body] <br>"
var/pic_data
if(MESSAGE.img)
user << browse_rsc(MESSAGE.img, "tmp_photo[i].png")
pic_data+="<img src='tmp_photo[i].png' width = '180'><BR>"
dat+= get_newspaper_content(MESSAGE.title, MESSAGE.body, MESSAGE.author,"#d4cec1", pic_data)
dat+="<BR>"
dat+=span_small("\[Story by [span_maroon("[MESSAGE.author] - [MESSAGE.time_stamp]")]\]") + "<BR>"
dat+="<br>"
dat+=span_small("\[Story by [span_maroon("[MESSAGE.author] - [MESSAGE.time_stamp]")]\]") + "<br>"
dat+={"
<BR><HR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_refresh=1'>Refresh</A>
<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[1]'>Back</A>
<br><HR><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_refresh=1'>Refresh</a>
<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[1]'>Back</a>
"}
if(10)
dat+={"
"} + span_bold("[using_map.company_name] Feed Censorship Tool") + {"<BR>
"} + span_small("NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.<BR>") + {"
"} + span_bold("[using_map.company_name] Feed Censorship Tool") + {"<br>
"} + span_small("NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.<br>") + {"
"} + span_small("Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it.") + {"
<HR>Select Feed channel to get Stories from:<BR>
<HR>Select Feed channel to get Stories from:<br>
"}
if(isemptylist(GLOB.news_network.network_channels))
dat+=span_italics("No feed channels found active...") + "<BR>"
dat+=span_italics("No feed channels found active...") + "<br>"
else
for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels)
dat+="<A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_pick_censor_channel=\ref[CHANNEL]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? (span_red("***")) : null]<BR>"
dat+="<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[0]'>Cancel</A>"
dat+="<a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_pick_censor_channel=[REF(CHANNEL)]'>[CHANNEL.channel_name]</a> [(CHANNEL.censored) ? (span_red("***")) : null]<br>"
dat+="<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[0]'>Cancel</a>"
if(11)
dat+={"
"} + span_bold("[using_map.company_name] D-Notice Handler") + {"<HR>
@@ -408,44 +408,44 @@ ADMIN_VERB(access_news_network, R_ADMIN|R_EVENT, "Access Newscaster Network", "A
"} + span_small("stories it might contain at the time. You can lift a D-Notice if you have the required access at any time.") + {"<HR>
"}
if(isemptylist(GLOB.news_network.network_channels))
dat+=span_italics("No feed channels found active...") + "<BR>"
dat+=span_italics("No feed channels found active...") + "<br>"
else
for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels)
dat+="<A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_pick_d_notice=\ref[CHANNEL]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? (span_red("***")) : null]<BR>"
dat+="<a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_pick_d_notice=[REF(CHANNEL)]'>[CHANNEL.channel_name]</a> [(CHANNEL.censored) ? (span_red("***")) : null]<br>"
dat+="<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[0]'>Back</A>"
dat+="<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[0]'>Back</a>"
if(12)
dat+={"
"} + span_bold("[admin_holder.admincaster_feed_channel.channel_name]: ") + span_small("\[ created by: [span_maroon("[admin_holder.admincaster_feed_channel.author]")] \]") + {"<BR>
"} + span_normal("<A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_censor_channel_author=\ref[admin_holder.admincaster_feed_channel]'>[(admin_holder.admincaster_feed_channel.author=="\[REDACTED\]") ? ("Undo Author censorship") : ("Censor channel Author")]</A>") + {"<HR>
"} + span_bold("[admin_holder.admincaster_feed_channel.channel_name]: ") + span_small("\[ created by: [span_maroon("[admin_holder.admincaster_feed_channel.author]")] \]") + {"<br>
"} + span_normal("<a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_censor_channel_author=[REF(admin_holder.admincaster_feed_channel)]'>[(admin_holder.admincaster_feed_channel.author=="\[REDACTED\]") ? ("Undo Author censorship") : ("Censor channel Author")]</a>") + {"<HR>
"}
if( isemptylist(admin_holder.admincaster_feed_channel.messages) )
dat+=span_italics("No feed messages found in channel...") + "<BR>"
dat+=span_italics("No feed messages found in channel...") + "<br>"
else
for(var/datum/feed_message/MESSAGE in admin_holder.admincaster_feed_channel.messages)
dat+={"
-[MESSAGE.body] <BR>"} + span_small("\[Story by [span_maroon("[MESSAGE.author]")]\]") + {"<BR>
"} + span_normal("<A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_censor_channel_story_body=\ref[MESSAGE]'>[(MESSAGE.body == "\[REDACTED\]") ? ("Undo story censorship") : ("Censor story")]</A> - <A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_censor_channel_story_author=\ref[MESSAGE]'>[(MESSAGE.author == "\[REDACTED\]") ? ("Undo Author Censorship") : ("Censor message Author")]</A>") + {"<BR>
-[MESSAGE.body] <br>"} + span_small("\[Story by [span_maroon("[MESSAGE.author]")]\]") + {"<br>
"} + span_normal("<a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_censor_channel_story_body=[REF(MESSAGE)]'>[(MESSAGE.body == "\[REDACTED\]") ? ("Undo story censorship") : ("Censor story")]</a> - <a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_censor_channel_story_author=[REF(MESSAGE)]'>[(MESSAGE.author == "\[REDACTED\]") ? ("Undo Author Censorship") : ("Censor message Author")]</a>") + {"<br>
"}
dat+="<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[10]'>Back</A>"
dat+="<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[10]'>Back</a>"
if(13)
dat+={"
"} + span_bold("[admin_holder.admincaster_feed_channel.channel_name]: ") + span_small("\[ created by: [span_maroon("[admin_holder.admincaster_feed_channel.author]")] \]") + {"<BR>
Channel messages listed below. If you deem them dangerous to the station, you can <A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_toggle_d_notice=\ref[admin_holder.admincaster_feed_channel]'>Bestow a D-Notice upon the channel</A>.<HR>
"} + span_bold("[admin_holder.admincaster_feed_channel.channel_name]: ") + span_small("\[ created by: [span_maroon("[admin_holder.admincaster_feed_channel.author]")] \]") + {"<br>
Channel messages listed below. If you deem them dangerous to the station, you can <a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_toggle_d_notice=[REF(admin_holder.admincaster_feed_channel)]'>Bestow a D-Notice upon the channel</a>.<HR>
"}
if(admin_holder.admincaster_feed_channel.censored)
dat+={"
"} + span_red(span_bold("ATTENTION: ")) + {"This channel has been deemed as threatening to the welfare of the station, and marked with a [using_map.company_name] D-Notice.<BR>
No further feed story additions are allowed while the D-Notice is in effect.<BR><BR>
"} + span_red(span_bold("ATTENTION: ")) + {"This channel has been deemed as threatening to the welfare of the station, and marked with a [using_map.company_name] D-Notice.<br>
No further feed story additions are allowed while the D-Notice is in effect.<br><br>
"}
else
if( isemptylist(admin_holder.admincaster_feed_channel.messages) )
dat+=span_italics("No feed messages found in channel...") + "<BR>"
dat+=span_italics("No feed messages found in channel...") + "<br>"
else
for(var/datum/feed_message/MESSAGE in admin_holder.admincaster_feed_channel.messages)
dat+="-[MESSAGE.body] <BR>" + span_small("\[Story by [span_maroon("[MESSAGE.author]")]\]") + "<BR>"
dat+="-[MESSAGE.body] <br>" + span_small("\[Story by [span_maroon("[MESSAGE.author]")]\]") + "<br>"
dat+="<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[11]'>Back</A>"
dat+="<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[11]'>Back</a>"
if(14)
dat+=span_bold("Wanted Issue Handler:")
var/wanted_already = 0
@@ -454,54 +454,54 @@ ADMIN_VERB(access_news_network, R_ADMIN|R_EVENT, "Access Newscaster Network", "A
wanted_already = 1
end_param = 2
if(wanted_already)
dat+=span_normal(span_italics("<BR>A wanted issue is already in Feed Circulation. You can edit or cancel it below."))
dat+=span_normal(span_italics("<br>A wanted issue is already in Feed Circulation. You can edit or cancel it below."))
dat+={"
<HR>
<A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_set_wanted_name=1'>Criminal Name</A>: [admin_holder.admincaster_feed_message.author] <BR>
<A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_set_wanted_desc=1'>Description</A>: [admin_holder.admincaster_feed_message.body] <BR>
<a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_set_wanted_name=1'>Criminal Name</a>: [admin_holder.admincaster_feed_message.author] <br>
<a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_set_wanted_desc=1'>Description</a>: [admin_holder.admincaster_feed_message.body] <br>
"}
if(wanted_already)
dat+=span_bold("Wanted Issue created by:") + span_green(" [GLOB.news_network.wanted_issue.backup_author]") + "<BR>"
dat+=span_bold("Wanted Issue created by:") + span_green(" [GLOB.news_network.wanted_issue.backup_author]") + "<br>"
else
dat+=span_bold("Wanted Issue will be created under prosecutor:") + span_green(" [admin_holder.admincaster_signature]") + "<BR>"
dat+="<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_submit_wanted=[end_param]'>[(wanted_already) ? ("Edit Issue") : ("Submit")]</A>"
dat+=span_bold("Wanted Issue will be created under prosecutor:") + span_green(" [admin_holder.admincaster_signature]") + "<br>"
dat+="<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_submit_wanted=[end_param]'>[(wanted_already) ? ("Edit Issue") : ("Submit")]</a>"
if(wanted_already)
dat+="<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_cancel_wanted=1'>Take down Issue</A>"
dat+="<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[0]'>Cancel</A>"
dat+="<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_cancel_wanted=1'>Take down Issue</a>"
dat+="<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[0]'>Cancel</a>"
if(15)
dat+={"
"} + span_green("Wanted issue for [admin_holder.admincaster_feed_message.author] is now in Network Circulation.") + {"<BR><BR>
<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[0]'>Return</A><BR>
"} + span_green("Wanted issue for [admin_holder.admincaster_feed_message.author] is now in Network Circulation.") + {"<br><br>
<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[0]'>Return</a><br>
"}
if(16)
dat+=span_bold(span_maroon("ERROR: Wanted Issue rejected by Network.")) + "<HR><BR>"
dat+=span_bold(span_maroon("ERROR: Wanted Issue rejected by Network.")) + "<HR><br>"
if(admin_holder.admincaster_feed_message.author =="" || admin_holder.admincaster_feed_message.author == "\[REDACTED\]")
dat+=span_maroon("•Invalid name for person wanted.") + "<BR>"
dat+=span_maroon("•Invalid name for person wanted.") + "<br>"
if(admin_holder.admincaster_feed_message.body == "" || admin_holder.admincaster_feed_message.body == "\[REDACTED\]")
dat+=span_maroon("•Invalid description.") + "<BR>"
dat+="<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[0]'>Return</A><BR>"
dat+=span_maroon("•Invalid description.") + "<br>"
dat+="<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[0]'>Return</a><br>"
if(17)
dat+={"
"} + span_bold("Wanted Issue successfully deleted from Circulation") + {"<BR>
<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[0]'>Return</A><BR>
"} + span_bold("Wanted Issue successfully deleted from Circulation") + {"<br>
<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[0]'>Return</a><br>
"}
if(18)
dat+={"
"} + span_bold(span_maroon("-- STATIONWIDE WANTED ISSUE --")) + {"<BR>"} + span_normal("\[Submitted by: [span_green("[GLOB.news_network.wanted_issue.backup_author]")]\]") + {"<HR>
"} + span_bold("Criminal") + {": [GLOB.news_network.wanted_issue.author]<BR>
"} + span_bold("Description") + {": [GLOB.news_network.wanted_issue.body]<BR>
"} + span_bold(span_maroon("-- STATIONWIDE WANTED ISSUE --")) + {"<br>"} + span_normal("\[Submitted by: [span_green("[GLOB.news_network.wanted_issue.backup_author]")]\]") + {"<HR>
"} + span_bold("Criminal") + {": [GLOB.news_network.wanted_issue.author]<br>
"} + span_bold("Description") + {": [GLOB.news_network.wanted_issue.body]<br>
"} + span_bold("Photo:") + {":
"}
if(GLOB.news_network.wanted_issue.img)
user << browse_rsc(GLOB.news_network.wanted_issue.img, "tmp_photow.png")
dat+="<BR><img src='tmp_photow.png' width = '180'>"
dat+="<br><img src='tmp_photow.png' width = '180'>"
else
dat+="None"
dat+="<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[0]'>Back</A><BR>"
dat+="<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[0]'>Back</a><br>"
if(19)
dat+={"
"} + span_green("Wanted issue for [admin_holder.admincaster_feed_message.author] successfully edited.") + {"<BR><BR>
<BR><A href='byond://?src=\ref[admin_holder];[HrefToken()];ac_setScreen=[0]'>Return</A><BR>
"} + span_green("Wanted issue for [admin_holder.admincaster_feed_message.author] successfully edited.") + {"<br><br>
<br><a href='byond://?src=[REF(admin_holder)];[HrefToken()];ac_setScreen=[0]'>Return</a><br>
"}
else
dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com"
@@ -523,7 +523,7 @@ ADMIN_VERB(access_news_network, R_ADMIN|R_EVENT, "Access Newscaster Network", "A
var/r = t
if( findtext(r,"##") )
r = copytext( r, 1, findtext(r,"##") )//removes the description
dat += text("<tr><td>[t] (<A href='byond://?src=\ref[src];[HrefToken()];removejobban=[r]'>unban</A>)</td></tr>")
dat += text("<tr><td>[t] (<a href='byond://?src=[REF(src)];[HrefToken()];removejobban=[r]'>unban</a>)</td></tr>")
dat += "</table>"
var/datum/browser/popup = new(owner, "ban", "Job Bans", 400, 400)
@@ -536,20 +536,17 @@ ADMIN_VERB(access_news_network, R_ADMIN|R_EVENT, "Access Newscaster Network", "A
var/dat = {"
<center>"} + span_bold("Game Panel") + {"</center><hr>\n
<A href='byond://?src=\ref[src];[HrefToken()];c_mode=1'>Change Game Mode</A><br>
<a href='byond://?src=[REF(src)];[HrefToken()];c_mode=1'>Change Game Mode</a><br>
"}
if(GLOB.master_mode == "secret")
dat += "<A href='byond://?src=\ref[src];[HrefToken()];f_secret=1'>(Force Secret Mode)</A><br>"
dat += "<a href='byond://?src=[REF(src)];[HrefToken()];f_secret=1'>(Force Secret Mode)</a><br>"
dat += {"
<BR>
<A href='byond://?src=\ref[src];[HrefToken()];create_object=1'>Create Object</A><br>
<A href='byond://?src=\ref[src];[HrefToken()];quick_create_object=1'>Quick Create Object</A><br>
<A href='byond://?src=\ref[src];[HrefToken()];create_turf=1'>Create Turf</A><br>
<A href='byond://?src=\ref[src];[HrefToken()];create_mob=1'>Create Mob</A><br>
<br><A href='byond://?src=\ref[src];[HrefToken()];vsc=airflow'>Edit Airflow Settings</A><br>
<A href='byond://?src=\ref[src];[HrefToken()];vsc=phoron'>Edit Phoron Settings</A><br>
<A href='byond://?src=\ref[src];[HrefToken()];vsc=default'>Choose a default ZAS setting</A><br>
<br>
<a href='byond://?src=[REF(src)];[HrefToken()];spawn_panel=1'>Spawn Panel</a><br>
<br><a href='byond://?src=[REF(src)];[HrefToken()];vsc=airflow'>Edit Airflow Settings</a><br>
<a href='byond://?src=[REF(src)];[HrefToken()];vsc=phoron'>Edit Phoron Settings</a><br>
<a href='byond://?src=[REF(src)];[HrefToken()];vsc=default'>Choose a default ZAS setting</a><br>
"}
var/datum/browser/popup = new(owner, "admin2", "Game Panel", 220, 295)
@@ -565,19 +562,19 @@ ADMIN_VERB(access_news_network, R_ADMIN|R_EVENT, "Access Newscaster Network", "A
for(var/datum/admin_secret_category/category in admin_secrets.categories)
if(!category.can_view(usr))
continue
dat += "<A href='byond://?src=\ref[src];[HrefToken()];admin_secrets_panel=\ref[category]'>[category.name]</A> "
dat += "<a href='byond://?src=[REF(src)];[HrefToken()];admin_secrets_panel=[REF(category)]'>[category.name]</a> "
dat += "<HR>"
// If a category is selected, print its description and then options
if(istype(active_category) && active_category.can_view(usr))
dat += span_bold("[active_category.name]") + "<BR>"
dat += span_bold("[active_category.name]") + "<br>"
if(active_category.desc)
dat += span_italics("[active_category.desc]") + "<BR>"
dat += span_italics("[active_category.desc]") + "<br>"
for(var/datum/admin_secret_item/item in active_category.items)
if(!item.can_view(usr))
continue
dat += "<A href='byond://?src=\ref[src];[HrefToken()];admin_secrets=\ref[item]'>[item.name()]</A><BR>"
dat += "<BR>"
dat += "<a href='byond://?src=[REF(src)];[HrefToken()];admin_secrets=[REF(item)]'>[item.name()]</a><br>"
dat += "<br>"
var/datum/browser/popup = new(usr, "secrets", "Secrets", 500, 500)
popup.set_content(dat)
@@ -1072,66 +1069,66 @@ ADMIN_VERB(show_game_mode, R_ADMIN|R_EVENT, "Show Game Mode", "Show the current
tgui_alert_async(user, "Not before roundstart!", "Alert")
return
var/out = span_large(span_bold("Current mode: [SSticker.mode.name] (<a href='byond://?src=\ref[SSticker.mode];[HrefToken()];debug_antag=self'>[SSticker.mode.config_tag]</a>)")) + "<br/>"
var/out = span_large(span_bold("Current mode: [SSticker.mode.name] (<a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];debug_antag=self'>[SSticker.mode.config_tag]</a>)")) + "<br/>"
out += "<hr>"
if(SSticker.mode.ert_disabled)
out += span_bold("Emergency Response Teams:") + "<a href='byond://?src=\ref[SSticker.mode];[HrefToken()];toggle=ert'>disabled</a>"
out += span_bold("Emergency Response Teams:") + "<a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];toggle=ert'>disabled</a>"
else
out += span_bold("Emergency Response Teams:") + "<a href='byond://?src=\ref[SSticker.mode];[HrefToken()];toggle=ert'>enabled</a>"
out += span_bold("Emergency Response Teams:") + "<a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];toggle=ert'>enabled</a>"
out += "<br/>"
if(SSticker.mode.deny_respawn)
out += span_bold("Respawning:") + "<a href='byond://?src=\ref[SSticker.mode];[HrefToken()];toggle=respawn'>disallowed</a>"
out += span_bold("Respawning:") + "<a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];toggle=respawn'>disallowed</a>"
else
out += span_bold("Respawning:") + "<a href='byond://?src=\ref[SSticker.mode];[HrefToken()];toggle=respawn'>allowed</a>"
out += span_bold("Respawning:") + "<a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];toggle=respawn'>allowed</a>"
out += "<br/>"
out += span_bold("Shuttle delay multiplier:") + " <a href='byond://?src=\ref[SSticker.mode];[HrefToken()];set=shuttle_delay'>[SSticker.mode.shuttle_delay]</a><br/>"
out += span_bold("Shuttle delay multiplier:") + " <a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];set=shuttle_delay'>[SSticker.mode.shuttle_delay]</a><br/>"
if(SSticker.mode.auto_recall_shuttle)
out += span_bold("Shuttle auto-recall:") + " <a href='byond://?src=\ref[SSticker.mode];[HrefToken()];toggle=shuttle_recall'>enabled</a>"
out += span_bold("Shuttle auto-recall:") + " <a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];toggle=shuttle_recall'>enabled</a>"
else
out += span_bold("Shuttle auto-recall:") + " <a href='byond://?src=\ref[SSticker.mode];[HrefToken()];toggle=shuttle_recall'>disabled</a>"
out += span_bold("Shuttle auto-recall:") + " <a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];toggle=shuttle_recall'>disabled</a>"
out += "<br/><br/>"
if(SSticker.mode.event_delay_mod_moderate)
out += span_bold("Moderate event time modifier:") + " <a href='byond://?src=\ref[SSticker.mode];[HrefToken()];set=event_modifier_moderate'>[SSticker.mode.event_delay_mod_moderate]</a><br/>"
out += span_bold("Moderate event time modifier:") + " <a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];set=event_modifier_moderate'>[SSticker.mode.event_delay_mod_moderate]</a><br/>"
else
out += span_bold("Moderate event time modifier:") + " <a href='byond://?src=\ref[SSticker.mode];[HrefToken()];set=event_modifier_moderate'>unset</a><br/>"
out += span_bold("Moderate event time modifier:") + " <a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];set=event_modifier_moderate'>unset</a><br/>"
if(SSticker.mode.event_delay_mod_major)
out += span_bold("Major event time modifier:") + " <a href='byond://?src=\ref[SSticker.mode];[HrefToken()];set=event_modifier_severe'>[SSticker.mode.event_delay_mod_major]</a><br/>"
out += span_bold("Major event time modifier:") + " <a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];set=event_modifier_severe'>[SSticker.mode.event_delay_mod_major]</a><br/>"
else
out += span_bold("Major event time modifier:") + " <a href='byond://?src=\ref[SSticker.mode];[HrefToken()];set=event_modifier_severe'>unset</a><br/>"
out += span_bold("Major event time modifier:") + " <a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];set=event_modifier_severe'>unset</a><br/>"
out += "<hr>"
if(SSticker.mode.antag_tags && SSticker.mode.antag_tags.len)
out += span_bold("Core antag templates:") + "</br>"
for(var/antag_tag in SSticker.mode.antag_tags)
out += "<a href='byond://?src=\ref[SSticker.mode];[HrefToken()];debug_antag=[antag_tag]'>[antag_tag]</a>.</br>"
out += "<a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];debug_antag=[antag_tag]'>[antag_tag]</a>.</br>"
if(SSticker.mode.round_autoantag)
out += span_bold("Autotraitor <a href='byond://?src=\ref[SSticker.mode];[HrefToken()];toggle=autotraitor'>enabled</a>.")
out += span_bold("Autotraitor <a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];toggle=autotraitor'>enabled</a>.")
if(SSticker.mode.antag_scaling_coeff > 0)
out += " (scaling with <a href='byond://?src=\ref[SSticker.mode];[HrefToken()];set=antag_scaling'>[SSticker.mode.antag_scaling_coeff]</a>)"
out += " (scaling with <a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];set=antag_scaling'>[SSticker.mode.antag_scaling_coeff]</a>)"
else
out += " (not currently scaling, <a href='byond://?src=\ref[SSticker.mode];[HrefToken()];set=antag_scaling'>set a coefficient</a>)"
out += " (not currently scaling, <a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];set=antag_scaling'>set a coefficient</a>)"
out += "<br/>"
else
out += span_bold("Autotraitor <a href='byond://?src=\ref[SSticker.mode];[HrefToken()];toggle=autotraitor'>disabled</a>.") + "<br/>"
out += span_bold("Autotraitor <a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];toggle=autotraitor'>disabled</a>.") + "<br/>"
out += span_bold("All antag ids:")
if(SSticker.mode.antag_templates && SSticker.mode.antag_templates.len)
for(var/datum/antagonist/antag in SSticker.mode.antag_templates)
antag.update_current_antag_max()
out += " <a href='byond://?src=\ref[SSticker.mode];[HrefToken()];debug_antag=[antag.id]'>[antag.id]</a>"
out += " <a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];debug_antag=[antag.id]'>[antag.id]</a>"
out += " ([antag.get_antag_count()]/[antag.cur_max]) "
out += " <a href='byond://?src=\ref[SSticker.mode];[HrefToken()];remove_antag_type=[antag.id]'>\[-\]</a><br/>"
out += " <a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];remove_antag_type=[antag.id]'>\[-\]</a><br/>"
else
out += " None."
out += " <a href='byond://?src=\ref[SSticker.mode];[HrefToken()];add_antag_type=1'>\[+\]</a><br/>"
out += " <a href='byond://?src=[REF(SSticker.mode)];[HrefToken()];add_antag_type=1'>\[+\]</a><br/>"
var/datum/browser/popup = new(user, "edit_mode[user.holder]", "Edit Game Mode")
popup.set_content(out)
@@ -1188,19 +1185,19 @@ ADMIN_VERB(toggleguests, R_HOST, "Toggle guests", "Guests can't enter.", ADMIN_C
return span_bold("[key_name(C, link, name, highlight_special)]")
if(1) //Private Messages
return span_bold("[key_name(C, link, name, highlight_special)](<A href='byond://?_src_=holder;[HrefToken()];adminmoreinfo=\ref[M]'>?</A>)")
return span_bold("[key_name(C, link, name, highlight_special)](<a href='byond://?_src_=holder;[HrefToken()];adminmoreinfo=[REF(M)]'>?</a>)")
if(2) //Admins
var/ref_mob = "\ref[M]"
return span_bold("[key_name(C, link, name, highlight_special)](<A href='byond://?_src_=holder;[HrefToken()];adminmoreinfo=[ref_mob]'>?</A>) (<A href='byond://?_src_=holder;[HrefToken()];adminplayeropts=[ref_mob]'>PP</A>) (<A href='byond://?_src_=vars;[HrefToken()];Vars=[ref_mob]'>VV</A>) (<A href='byond://?_src_=holder;[HrefToken()];subtlemessage=[ref_mob]'>SM</A>) ([admin_jump_link(M)]) (<A href='byond://?_src_=holder;[HrefToken()];check_antagonist=1'>CA</A>) (<A href='byond://?_src_=holder;[HrefToken()];take_question=\ref[M]'>TAKE</A>)")
var/ref_mob = "[REF(M)]"
return span_bold("[key_name(C, link, name, highlight_special)](<a href='byond://?_src_=holder;[HrefToken()];adminmoreinfo=[ref_mob]'>?</a>) (<a href='byond://?_src_=holder;[HrefToken()];adminplayeropts=[ref_mob]'>PP</a>) (<a href='byond://?_src_=vars;[HrefToken()];Vars=[ref_mob]'>VV</a>) (<a href='byond://?_src_=holder;[HrefToken()];subtlemessage=[ref_mob]'>SM</a>) ([admin_jump_link(M)]) (<a href='byond://?_src_=holder;[HrefToken()];check_antagonist=1'>CA</a>) (<a href='byond://?_src_=holder;[HrefToken()];take_question=[REF(M)]'>TAKE</a>)")
if(3) //Devs
var/ref_mob = "\ref[M]"
return span_bold("[key_name(C, link, name, highlight_special)](<A href='byond://?_src_=vars;[HrefToken()];Vars=[ref_mob]'>VV</A>)([admin_jump_link(M)]) (<A href='byond://?_src_=holder;[HrefToken()];take_question=\ref[M]'>TAKE</A>)")
var/ref_mob = "[REF(M)]"
return span_bold("[key_name(C, link, name, highlight_special)](<a href='byond://?_src_=vars;[HrefToken()];Vars=[ref_mob]'>VV</a>)([admin_jump_link(M)]) (<a href='byond://?_src_=holder;[HrefToken()];take_question=[REF(M)]'>TAKE</a>)")
if(4) //Event Managers
var/ref_mob = "\ref[M]"
return span_bold("[key_name(C, link, name, highlight_special)] (<A href='byond://?_src_=holder;[HrefToken()];adminmoreinfo=\ref[M]'>?</A>) (<A href='byond://?_src_=holder;[HrefToken()];adminplayeropts=[ref_mob]'>PP</A>) (<A href='byond://?_src_=vars;[HrefToken()];Vars=[ref_mob]'>VV</A>) (<A href='byond://?_src_=holder;[HrefToken()];subtlemessage=[ref_mob]'>SM</A>) ([admin_jump_link(M)]) (<A href='byond://?_src_=holder;[HrefToken()];take_question=\ref[M]'>TAKE</A>)")
var/ref_mob = "[REF(M)]"
return span_bold("[key_name(C, link, name, highlight_special)] (<a href='byond://?_src_=holder;[HrefToken()];adminmoreinfo=[REF(M)]'>?</a>) (<a href='byond://?_src_=holder;[HrefToken()];adminplayeropts=[ref_mob]'>PP</a>) (<a href='byond://?_src_=vars;[HrefToken()];Vars=[ref_mob]'>VV</a>) (<a href='byond://?_src_=holder;[HrefToken()];subtlemessage=[ref_mob]'>SM</a>) ([admin_jump_link(M)]) (<a href='byond://?_src_=holder;[HrefToken()];take_question=[REF(M)]'>TAKE</a>)")
/proc/ishost(whom)
@@ -1369,12 +1366,12 @@ ADMIN_VERB(sendFax, R_ADMIN|R_MOD|R_EVENT, "Send Fax", "Sends a fax to this mach
log_admin("[key_name(src.owner)] replied to a fax message from [key_name(P.sender)]")
for(var/client/C in GLOB.admins)
if(check_rights_for(C, (R_ADMIN | R_MOD | R_EVENT)))
to_chat(C, span_log_message("[span_prefix("FAX LOG:")][key_name_admin(src.owner)] replied to a fax message from [key_name_admin(P.sender)] (<a href='byond://?_src_=holder;[HrefToken()];AdminFaxView=\ref[rcvdcopy]'>VIEW</a>)"))
to_chat(C, span_log_message("[span_prefix("FAX LOG:")][key_name_admin(src.owner)] replied to a fax message from [key_name_admin(P.sender)] (<a href='byond://?_src_=holder;[HrefToken()];AdminFaxView=[REF(rcvdcopy)]'>VIEW</a>)"))
else
log_admin("[key_name(src.owner)] has sent a fax message to [destination.department]")
for(var/client/C in GLOB.admins)
if(check_rights_for(C, (R_ADMIN | R_MOD | R_EVENT)))
to_chat(C, span_log_message("[span_prefix("FAX LOG:")][key_name_admin(src.owner)] has sent a fax message to [destination.department] (<a href='byond://?_src_=holder;[HrefToken()];AdminFaxView=\ref[rcvdcopy]'>VIEW</a>)"))
to_chat(C, span_log_message("[span_prefix("FAX LOG:")][key_name_admin(src.owner)] has sent a fax message to [destination.department] (<a href='byond://?_src_=holder;[HrefToken()];AdminFaxView=[REF(rcvdcopy)]'>VIEW</a>)"))
var/plaintext_title = P.sender ? "replied to [key_name(P.sender)]'s fax" : "sent a fax message to [destination.department]"
var/fax_text = paper_html_to_plaintext(P.info)
-9
View File
@@ -1,9 +0,0 @@
/var/create_mob_html = null
/datum/admins/proc/create_mob(var/mob/user)
if (!create_mob_html)
var/mobjs = null
mobjs = jointext(typesof(/mob), ";")
create_mob_html = file2text('html/create_object.html')
create_mob_html = replacetext(create_mob_html, "null /* object types */", "\"[mobjs]\"")
user << browse(create_panel_helper(create_mob_html), "window=create_mob;size=680x600")
-47
View File
@@ -1,47 +0,0 @@
/datum/admins/proc/create_panel_helper(template)
var/final_html = replacetext(template, "/* ref src */", "\ref[src];[HrefToken()]")
final_html = replacetext(final_html,"/* hreftokenfield */","[HrefTokenFormField()]")
return final_html
/datum/admins/proc/create_object(var/mob/user)
var/static/create_object_html = null
if (!create_object_html)
var/objectjs = null
objectjs = jointext(typesof(/obj), ";")
create_object_html = file2text('html/create_object.html')
create_object_html = replacetext(create_object_html, "null /* object types */", "\"[objectjs]\"")
user << browse(create_panel_helper(create_object_html), "window=create_object;size=680x600")
/datum/admins/proc/quick_create_object(var/mob/user)
var/quick_create_object_html = null
var/pathtext = null
var/list/choices = list("/obj",
"/obj/structure",
"/obj/item",
"/obj/item/melee",
"/obj/item/gun",
"/obj/item/reagent_containers",
"/obj/item/reagent_containers/food",
"/obj/item/clothing",
"/obj/item/storage/box/fluff", //VOREStation Edit,
"/obj/machinery",
"/obj/mecha",
"/obj/item/mecha_parts",
"/obj/item/mecha_parts/mecha_equipment")
pathtext = tgui_input_list(usr, "Select the path of the object you wish to create.", "Path", choices, "/obj")
if(!pathtext)
return
var path = text2path(pathtext)
if (!quick_create_object_html)
var/objectjs = null
objectjs = jointext(typesof(path), ";")
quick_create_object_html = file2text('html/create_object.html')
quick_create_object_html = replacetext(quick_create_object_html, "null /* object types */", "\"[objectjs]\"")
user << browse(create_panel_helper(quick_create_object_html), "window=quick_create_object;size=680x600")
-9
View File
@@ -1,9 +0,0 @@
/var/create_turf_html = null
/datum/admins/proc/create_turf(var/mob/user)
if (!create_turf_html)
var/turfjs = null
turfjs = jointext(typesof(/turf), ";")
create_turf_html = file2text('html/create_object.html')
create_turf_html = replacetext(create_turf_html, "null /* object types */", "\"[turfjs]\"")
user << browse(create_panel_helper(create_turf_html), "window=create_turf;size=680x600")
+1
View File
@@ -32,6 +32,7 @@ GLOBAL_PROTECT(href_token)
var/datum/filter_editor/filteriffic
var/datum/particle_editor/particle_test
var/datum/whitelist_editor/whitelist_editor
var/datum/spawnpanel/spawn_panel
/// A lazylist of tagged datums, for quick reference with the View Tags verb
var/list/tagged_datums
+6 -55
View File
@@ -219,63 +219,14 @@
target.module_reset(FALSE)
return TRUE
if("add_module")
var/obj/item/add_item = locate(params["module"])
if(!add_item)
var/obj/item/selected_item = locate(params["module"])
if(!selected_item)
return TRUE
if(istype(add_item, /obj/item/card/id))
if(istype(selected_item, /obj/item/card/id))
source.idcard = null
source.module.emag.Remove(add_item)
source.module.modules.Remove(add_item)
source.module.contents.Remove(add_item)
if(istype(add_item, /obj/item/card/id))
if(target.idcard)
qdel(target.idcard)
target.idcard = add_item
target.module.modules.Add(add_item)
target.module.contents.Add(add_item)
spawn(0)
SEND_SIGNAL(add_item, COMSIG_MOVABLE_ATTEMPTED_MOVE)
target.hud_used?.update_robot_modules_display()
if(istype(add_item, /obj/item/stack/))
var/obj/item/stack/item_with_synth = add_item
for(var/synth in item_with_synth.synths)
var/found = target.module.synths.Find(synth)
if(!found)
source.module.synths.Remove(synth)
target.module.synths.Add(synth)
else
item_with_synth.synths = list(target.module.synths[found])
return TRUE
if(istype(add_item, /obj/item/matter_decompiler/) || istype(add_item, /obj/item/dogborg/sleeper/compactor/decompiler/))
var/obj/item/matter_decompiler/item_with_matter = add_item
if(item_with_matter.metal)
var/found = target.module.synths.Find(item_with_matter.metal)
if(!found)
source.module.synths.Remove(item_with_matter.metal)
target.module.synths.Add(item_with_matter.metal)
else
item_with_matter.metal = target.module.synths[found]
if(item_with_matter.glass)
var/found = target.module.synths.Find(item_with_matter.glass)
if(!found)
source.module.synths.Remove(item_with_matter.glass)
target.module.synths.Add(item_with_matter.glass)
else
item_with_matter.glass = target.module.synths[found]
if(item_with_matter.wood)
var/found = target.module.synths.Find(item_with_matter.wood)
if(!found)
source.module.synths.Remove(item_with_matter.wood)
target.module.synths.Add(item_with_matter.wood)
else
item_with_matter.wood = target.module.synths[found]
if(item_with_matter.plastic)
var/found = target.module.synths.Find(item_with_matter.plastic)
if(!found)
source.module.synths.Remove(item_with_matter.plastic)
target.module.synths.Add(item_with_matter.plastic)
else
item_with_matter.plastic = target.module.synths[found]
source.module.emag -= selected_item
source.module.modules -= selected_item
target.module.add_item(selected_item, target)
return TRUE
if("rem_module")
var/obj/item/rem_item = locate(params["module"])
@@ -0,0 +1,9 @@
// Open "View Variables" menu for target
/mob/observer/dead/CtrlShiftClickOn(atom/target)
if(check_rights(R_DEBUG))
SSadmin_verbs.dynamic_invoke_verb(client, /datum/admin_verb/debug_variables, target)
// Open "Show Player Panel" menu for target mob
/mob/observer/dead/CtrlClickOn(atom/target)
if(check_rights(R_ADMIN) && ismob(target))
SSadmin_verbs.dynamic_invoke_verb(client, /datum/admin_verb/show_player_panel, target)
+51
View File
@@ -0,0 +1,51 @@
/datum/asset/json/spawnpanel
name = "spawnpanel_atom_data"
/datum/asset/json/spawnpanel/generate()
var/list/data = list()
var/static/list/mapping_objects = typecacheof(list(
/obj/effect/landmark,
/obj/effect/spawner,
/obj/effect/shuttle_landmark,
/obj/effect/floor_decal,
/obj/effect/decal,
/obj/effect/overlay,
/obj/effect/step_trigger,
/obj/effect/overmap,
/obj/effect/zone_divider,
))
data["atoms"] = list()
for(var/obj/each_object as anything in typesof(/obj))
data["atoms"][each_object] = list(
"icon" = /* each_object?.icon_preview ||*/ each_object?.icon || "none",
"icon_state" = /* each_object?.icon_state_preview ||*/ each_object?.icon_state || "none",
"name" = each_object.name,
"description" = each_object.desc,
"mapping" = is_type_in_typecache(each_object, mapping_objects),
"type" = "Objects"
)
for(var/turf/each_turf as anything in typesof(/turf))
data["atoms"][each_turf] = list(
"icon" = each_turf?.icon || "noneturf",
"icon_state" = each_turf?.icon_state || "noneturf",
"name" = each_turf.name,
"description" = each_turf.desc,
"mapping" = is_type_in_typecache(each_turf, mapping_objects),
"type" = "Turfs"
)
for(var/mob/each_mob as anything in typesof(/mob))
data["atoms"][each_mob] = list(
"icon" = each_mob?.icon || "nonemob",
"icon_state" = each_mob?.icon_state || "nonemob",
"name" = each_mob.name,
"description" = each_mob.desc,
"mapping" = is_type_in_typecache(each_mob, mapping_objects),
"type" = "Mobs"
)
return data
@@ -0,0 +1,161 @@
#define WHERE_FLOOR_BELOW_MOB "Current location"
#define WHERE_SUPPLY_BELOW_MOB "Current location (droppod)"
#define WHERE_MOB_HAND "In own mob's hand"
#define WHERE_MARKED_OBJECT "At a marked object"
#define WHERE_IN_MARKED_OBJECT "In the marked object"
#define WHERE_TARGETED_LOCATION "Targeted location"
#define WHERE_TARGETED_LOCATION_POD "Targeted location (droppod)"
#define WHERE_TARGETED_MOB_HAND "In targeted mob's hand"
#define OFFSET_ABSOLUTE "Absolute offset"
#define OFFSET_RELATIVE "Relative offset"
/*
Handles spawning an atom. See the call examples for the proper spawn parameters fetching.
*/
/datum/spawnpanel/proc/spawn_atom(list/spawn_params, mob/user)
if(!check_rights_for(user.client, R_SPAWN) || !spawn_params)
return
var/atom/atom_to_spawn = spawn_params["selected_atom"]
if(!atom_to_spawn || (!ispath(atom_to_spawn, /obj) && !ispath(atom_to_spawn, /turf) && !ispath(atom_to_spawn, /mob)))
return
var/amount = clamp(text2num(spawn_params["atom_amount"]), 1, ADMIN_SPAWN_CAP)
var/X = offset["X"] || 0
var/Y = offset["Y"] || 0
var/Z = offset["Z"] || 0
var/atom_dir = text2num(spawn_params["atom_dir"]) || 1
var/atom_name = sanitize(spawn_params["atom_name"])
var/where_target_type = spawn_params["where_target_type"]
var/atom/target = null
if(where_target_type == WHERE_MOB_HAND || where_target_type == WHERE_TARGETED_MOB_HAND)
target = (where_target_type == WHERE_TARGETED_MOB_HAND ? spawn_params["target"] : user)
if(!target)
to_chat(user, span_warning("No target specified."))
return
if(!ismob(target))
to_chat(user, span_warning("The targeted atom is not a mob."))
return
if(!iscarbon(target) && !isrobot(target))
to_chat(user, span_warning("Can only spawn in hand when the target is a carbon mob or a cyborg."))
where_target_type = WHERE_FLOOR_BELOW_MOB
else if(where_target_type == WHERE_MARKED_OBJECT || where_target_type == WHERE_IN_MARKED_OBJECT)
if(!user.client.holder.marked_datum)
to_chat(user, span_warning("You don't have any object marked."))
return
else if(!istype(user.client.holder.marked_datum, /atom))
to_chat(user, span_warning("The object you have marked cannot be used as a target. Target must be of type /atom."))
return
else
target = (where_target_type == WHERE_MARKED_OBJECT ? get_turf(user.client.holder.marked_datum) : user.client.holder.marked_datum)
else if(where_target_type == WHERE_TARGETED_LOCATION || where_target_type == WHERE_TARGETED_LOCATION_POD)
target = spawn_params["target"]
else
switch(spawn_params["offset_type"])
if(OFFSET_ABSOLUTE)
target = locate(X, Y, Z)
if(OFFSET_RELATIVE)
var/turf/relative_turf
var/atom/user_loc = user.loc
if (user_loc)
relative_turf = get_turf(user_loc)
if (!relative_turf)
if(isobserver(user))
var/mob/observer/dead/user_observer = user
relative_turf = get_turf(user_observer.client?.eye) || get_turf(user_observer)
if (!relative_turf)
relative_turf = locate(1, 1, 1)
if (!relative_turf)
to_chat(user, span_warning("Could not determine a valid relative location."))
return
target = locate(relative_turf.x + X, relative_turf.y + Y, relative_turf.z + Z)
if(!target)
return
var/use_droppod = where_target_type == WHERE_SUPPLY_BELOW_MOB || where_target_type == WHERE_TARGETED_LOCATION_POD
var/obj/structure/drop_pod/polite/pod
if(use_droppod)
pod = new(target)
for(var/i in 1 to amount)
if(istype(atom_to_spawn, /turf))
var/turf/original_turf = target
var/turf/created_turf = original_turf.ChangeTurf(atom_to_spawn.type)
if(created_turf && atom_name)
created_turf.name = atom_name
continue
var/atom/created_atom = new atom_to_spawn(use_droppod ? pod : target)
if(QDELETED(created_atom))
return
created_atom.flags |= ADMIN_SPAWNED
if(spawn_params["apply_icon_override"])
if(spawn_params["selected_atom_icon"])
created_atom.icon = file(spawn_params["selected_atom_icon"])
if(spawn_params["selected_atom_icon_state"])
created_atom.icon_state = spawn_params["selected_atom_icon_state"]
if(spawn_params["atom_icon_size"])
if(ismob(created_atom))
var/mob/living/created_mob = created_atom
created_mob.size_multiplier = spawn_params["atom_icon_size"] / 100
if(atom_dir)
created_atom.set_dir(atom_dir)
if(atom_name)
created_atom.name = atom_name
if(ismob(created_atom))
var/mob/created_mob = created_atom
created_mob.real_name = atom_name
if((where_target_type == WHERE_MOB_HAND || where_target_type == WHERE_TARGETED_MOB_HAND) && isliving(target) && isitem(created_atom))
var/mob/living/living_target = target
var/obj/item/created_item = created_atom
living_target.put_in_hands(created_item)
if(isrobot(living_target))
var/mob/living/silicon/robot/target_robot = living_target
if(target_robot.module)
target_robot.module.add_item(created_item, target_robot)
target_robot.activate_module(created_item)
if(pod)
pod.podfall()
log_admin("[key_name(user)] created [amount == 1 ? "an instance" : "[amount] instances"] of [atom_to_spawn.type]")
if(istype(atom_to_spawn, /mob))
message_admins("[key_name_admin(user)] created [amount == 1 ? "an instance" : "[amount] instances"] of [atom_to_spawn.type]")
#undef WHERE_FLOOR_BELOW_MOB
#undef WHERE_SUPPLY_BELOW_MOB
#undef WHERE_MOB_HAND
#undef WHERE_MARKED_OBJECT
#undef WHERE_IN_MARKED_OBJECT
#undef WHERE_TARGETED_LOCATION
#undef WHERE_TARGETED_LOCATION_POD
#undef WHERE_TARGETED_MOB_HAND
#undef OFFSET_ABSOLUTE
#undef OFFSET_RELATIVE
@@ -0,0 +1,304 @@
#define WHERE_FLOOR_BELOW_MOB "Current location"
#define WHERE_SUPPLY_BELOW_MOB "Current location (droppod)"
#define WHERE_MOB_HAND "In own mob's hand"
#define WHERE_MARKED_OBJECT "At a marked object"
#define WHERE_IN_MARKED_OBJECT "In the marked object"
#define WHERE_TARGETED_LOCATION "Targeted location"
#define WHERE_TARGETED_LOCATION_POD "Targeted location (droppod)"
#define WHERE_TARGETED_MOB_HAND "In targeted mob's hand"
#define PRECISE_MODE_OFF "Off"
#define PRECISE_MODE_TARGET "Target"
#define PRECISE_MODE_MARK "Mark"
#define PRECISE_MODE_COPY "Copy"
#define OFFSET_ABSOLUTE "Absolute offset"
#define OFFSET_RELATIVE "Relative offset"
/*
An instance of a /tg/UI™ Spawn Panel. Stores preferences, spawns things, controls the UI. Unique for each user (their ckey).
*/
/datum/spawnpanel
/// Where and how the atom should be spawned.
var/where_target_type = WHERE_FLOOR_BELOW_MOB
/// The atom selected from the panel.
var/atom/selected_atom = null
/// The icon selected for the atom from the panel.
var/selected_atom_icon = null
/// The icon state selected for the atom from the panel.
var/selected_atom_icon_state = null
/// Should selected icon/icon state override the initial ones? Added as an edge case to not replace animated GAGS icons.
var/apply_icon_override = FALSE
/// A list of icon states to display in preview panels.
var/list/available_icon_states = null
/// Override for the icon size of the spawned mob.
var/atom_icon_size = 100
/// How many atoms will be spawned at once.
var/atom_amount = 1
/// Custom atom name (leave `null` for initial).
var/atom_name = null
/// Custom atom description (leave `null` for initial).
var/atom_desc = null
/// Custom atom dir (leave `null` for `2`).
var/atom_dir = 1
/// An associative list of x-y-z offsets.
var/offset = list()
/// The pivot point for offsetting — relative or absolute.
var/offset_type = OFFSET_RELATIVE
/// Precise mode toggle. Used for build-mode-like spawning experience and targeting datums.
var/precise_mode = PRECISE_MODE_OFF
/datum/spawnpanel/New()
. = ..()
offset = list("X" = 0, "Y" = 0, "Z" = 0)
/datum/spawnpanel/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "SpawnPanel")
ui.open()
/datum/spawnpanel/tgui_close(mob/user)
. = ..()
if (precise_mode && precise_mode != PRECISE_MODE_OFF)
toggle_precise_mode(PRECISE_MODE_OFF, user)
/datum/spawnpanel/tgui_state(mob/user)
return ADMIN_STATE(R_SPAWN)
/datum/spawnpanel/tgui_act(action, params, datum/tgui/ui)
if(..() || !check_rights_for(ui.user.client, R_SPAWN))
return FALSE
switch(action)
if("select-new-DMI")
var/icon/new_icon = input("Select a new icon file:", "Icon") as null|icon
if(new_icon)
selected_atom_icon = new_icon
available_icon_states = icon_states(selected_atom_icon)
if(!(selected_atom_icon_state in available_icon_states))
selected_atom_icon_state = available_icon_states[1]
return TRUE
if("set-apply-icon-override")
apply_icon_override = !!params["value"]
return TRUE
if("reset-DMI-icon")
selected_atom_icon = null
selected_atom_icon_state = null
if(selected_atom)
selected_atom_icon = initial(selected_atom.icon)
selected_atom_icon_state = initial(selected_atom.icon_state)
available_icon_states = icon_states(selected_atom_icon)
else
available_icon_states = list()
return TRUE
if("select-new-icon-state")
selected_atom_icon_state = params["new_state"]
return TRUE
if("reset-icon-state")
selected_atom_icon_state = null
if(selected_atom)
selected_atom_icon_state = initial(selected_atom.icon_state)
return TRUE
if("set-icon-size")
atom_icon_size = params["size"]
return TRUE
if("reset-icon-size")
atom_icon_size = 100
return TRUE
if("get-icon-states")
available_icon_states = icon_states(selected_atom_icon)
return TRUE
if("selected-atom-changed")
var/path = text2path(params["newObj"])
if(path)
var/atom/temp_atom = path
selected_atom_icon = initial(temp_atom.icon)
selected_atom_icon_state = initial(temp_atom.icon_state)
available_icon_states = icon_states(selected_atom_icon)
selected_atom = temp_atom
return TRUE
if("create-atom-action")
var/list/spawn_params = list(
"selected_atom" = selected_atom,
"offset" = params["offset"],
"atom_dir" = text2num(params["dir"]) || 1,
"atom_amount" = text2num(params["atom_amount"]) || 1,
"atom_name" = params["atom_name"],
"where_target_type" = params["where_target_type"] || WHERE_FLOOR_BELOW_MOB,
"atom_icon_size" = params["atom_icon_size"],
"offset_type" = params["offset_type"] || OFFSET_RELATIVE,
"apply_icon_override" = apply_icon_override,
)
if(apply_icon_override)
spawn_params["selected_atom_icon"] = selected_atom_icon
spawn_params["selected_atom_icon_state"] = selected_atom_icon_state
spawn_atom(spawn_params, ui.user)
return TRUE
if("toggle-precise-mode")
var/precise_type = params["newPreciseType"]
if(precise_type == PRECISE_MODE_TARGET && params["where_target_type"])
where_target_type = params["where_target_type"]
toggle_precise_mode(precise_type, ui.user)
return TRUE
if("update-settings")
if(params["atom_amount"])
atom_amount = text2num(params["atom_amount"])
if(params["atom_dir"])
atom_dir = text2num(params["atom_dir"])
if(params["offset"])
var/list/temp_offset = params["offset"]
offset["X"] = temp_offset[1]
offset["Y"] = temp_offset[2]
offset["Z"] = temp_offset[3]
if(params["atom_name"])
atom_name = params["atom_name"]
if(params["where_target_type"])
where_target_type = params["where_target_type"]
if(params["offset_type"])
offset_type = params["offset_type"]
if(params["atom_icon_size"])
atom_icon_size = text2num(params["atom_icon_size"])
if(params["selected_atom_icon"])
selected_atom_icon = params["selected_atom_icon"]
if(params["selected_atom_icon_state"])
selected_atom_icon_state = params["selected_atom_icon_state"]
return TRUE
/datum/spawnpanel/proc/toggle_precise_mode(precise_type, mob/user)
precise_mode = precise_type
var/client/admin_client = user.client
if (!admin_client)
return
/* Unimplemented
admin_client.mouse_up_icon = null
admin_client.mouse_down_icon = null
admin_client.mouse_override_icon = null
*/
admin_client.click_intercept = null
if (precise_mode != PRECISE_MODE_OFF)
/* Unimplemented
admin_client.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_pickturf.dmi'
admin_client.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_pickturf_down.dmi'
admin_client.mouse_override_icon = admin_client.mouse_up_icon
admin_client.mouse_pointer_icon = admin_client.mouse_override_icon
*/
admin_client.click_intercept = src
winset(admin_client, "mapwindow.map", "right-click=true")
else
winset(admin_client, "mapwindow.map", "right-click=false")
/* Unimplemented
var/mob/holder_mob = admin_client.mob
holder_mob?.update_mouse_pointer()
*/
/datum/spawnpanel/proc/InterceptClickOn(mob/user, params, atom/target)
var/list/modifiers = params2list(params)
var/left_click = LAZYACCESS(modifiers, LEFT_CLICK)
var/right_click = LAZYACCESS(modifiers, RIGHT_CLICK)
if(right_click)
toggle_precise_mode(PRECISE_MODE_OFF, user)
SStgui.update_uis(src)
return TRUE
if(left_click)
if(istype(target,/atom/movable/screen))
return FALSE
var/turf/clicked_turf = get_turf(target)
if(!clicked_turf)
return FALSE
switch(precise_mode)
if(PRECISE_MODE_TARGET)
var/list/spawn_params = list(
"selected_atom" = selected_atom,
"atom_amount" = atom_amount,
"offset" = "0,0,0",
"atom_dir" = atom_dir,
"atom_name" = atom_name,
"atom_desc" = atom_desc,
"offset_type" = OFFSET_ABSOLUTE,
"where_target_type" = where_target_type,
"target" = target,
"atom_icon_size" = atom_icon_size,
"apply_icon_override" = apply_icon_override,
)
if(apply_icon_override)
spawn_params["selected_atom_icon"] = selected_atom_icon
spawn_params["selected_atom_icon_state"] = selected_atom_icon_state
if(where_target_type == WHERE_TARGETED_LOCATION || where_target_type == WHERE_TARGETED_LOCATION_POD)
spawn_params["X"] = clicked_turf.x
spawn_params["Y"] = clicked_turf.y
spawn_params["Z"] = clicked_turf.z
spawn_atom(spawn_params, user)
if(PRECISE_MODE_MARK)
var/client/admin_client = user.client
admin_client.mark_datum(target)
to_chat(user, span_notice("Marked object: [icon2html(target, user)] [span_bold("[target]")]"))
toggle_precise_mode(PRECISE_MODE_OFF, user)
SStgui.update_uis(src)
if(PRECISE_MODE_COPY)
to_chat(user, span_notice("Picked object: [icon2html(target, user)] [span_bold("[target]")]"))
selected_atom = target
toggle_precise_mode(PRECISE_MODE_OFF, user)
SStgui.update_uis(src)
return TRUE
/datum/spawnpanel/tgui_data(mob/user)
var/data = list()
data["icon"] = selected_atom_icon
data["iconState"] = selected_atom_icon_state
data["iconSize"] = atom_icon_size
data["apply_icon_override"] = apply_icon_override
var/list/states = list()
if(available_icon_states)
for(var/state in available_icon_states)
states += state
data["iconStates"] = states
data["precise_mode"] = precise_mode
data["selected_object"] = selected_atom ? "[selected_atom.type]" : ""
return data
/datum/spawnpanel/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/json/spawnpanel),
)
#undef WHERE_FLOOR_BELOW_MOB
#undef WHERE_SUPPLY_BELOW_MOB
#undef WHERE_MOB_HAND
#undef WHERE_MARKED_OBJECT
#undef WHERE_IN_MARKED_OBJECT
#undef WHERE_TARGETED_LOCATION
#undef WHERE_TARGETED_LOCATION_POD
#undef WHERE_TARGETED_MOB_HAND
#undef PRECISE_MODE_OFF
#undef PRECISE_MODE_TARGET
#undef PRECISE_MODE_MARK
#undef PRECISE_MODE_COPY
#undef OFFSET_ABSOLUTE
#undef OFFSET_RELATIVE
+2 -132
View File
@@ -1534,138 +1534,8 @@
return
SSadmin_verbs.dynamic_invoke_verb(usr.client, /datum/admin_verb/show_traitor_panel, M)
else if(href_list["create_object"])
if(!check_rights(R_SPAWN)) return
return create_object(usr)
else if(href_list["quick_create_object"])
if(!check_rights(R_SPAWN)) return
return quick_create_object(usr)
else if(href_list["create_turf"])
if(!check_rights(R_SPAWN)) return
return create_turf(usr)
else if(href_list["create_mob"])
if(!check_rights(R_SPAWN)) return
return create_mob(usr)
else if(href_list["object_list"]) //this is the laggiest thing ever
if(!check_rights(R_SPAWN)) return
if(!CONFIG_GET(flag/allow_admin_spawning))
to_chat(usr, span_filter_adminlog("Spawning of items is not allowed."))
return
var/atom/loc = usr.loc
var/dirty_paths
if (istext(href_list["object_list"]))
dirty_paths = list(href_list["object_list"])
else if (istype(href_list["object_list"], /list))
dirty_paths = href_list["object_list"]
var/paths = list()
var/removed_paths = list()
for(var/dirty_path in dirty_paths)
var/path = text2path(dirty_path)
if(!path)
removed_paths += dirty_path
continue
else if(!ispath(path, /obj) && !ispath(path, /turf) && !ispath(path, /mob))
removed_paths += dirty_path
continue
else if(ispath(path, /obj/item/gun/energy/pulse_rifle))
if(!check_rights(R_FUN,0))
removed_paths += dirty_path
continue
else if(ispath(path, /obj/item/melee/energy/blade))//Not an item one should be able to spawn./N
if(!check_rights(R_FUN,0))
removed_paths += dirty_path
continue
else if(ispath(path, /obj/effect/bhole))
if(!check_rights(R_FUN,0))
removed_paths += dirty_path
continue
paths += path
if(!paths)
tgui_alert(usr, "The path list you sent is empty")
return
if(length(paths) > 5)
tgui_alert_async(usr, "Select fewer object types, (max 5)")
return
else if(length(removed_paths))
tgui_alert_async(usr, "Removed:\n" + jointext(removed_paths, "\n"))
var/list/offset = splittext(href_list["offset"],",")
var/number = dd_range(1, 100, text2num(href_list["object_count"]))
var/X = offset.len > 0 ? text2num(offset[1]) : 0
var/Y = offset.len > 1 ? text2num(offset[2]) : 0
var/Z = offset.len > 2 ? text2num(offset[3]) : 0
var/tmp_dir = href_list["object_dir"]
var/obj_dir = tmp_dir ? text2num(tmp_dir) : 2
if(!obj_dir || !(obj_dir in list(1,2,4,8,5,6,9,10)))
obj_dir = 2
var/obj_name = sanitize(href_list["object_name"])
var/where = href_list["object_where"]
if (!( where in list("onfloor","inhand","inmarked") ))
where = "onfloor"
if( where == "inhand" )
to_chat(usr, span_filter_adminlog("Support for inhand not available yet. Will spawn on floor."))
where = "onfloor"
if ( where == "inhand" ) //Can only give when human or monkey
if ( !( ishuman(usr) || issmall(usr) ) )
to_chat(usr, span_filter_adminlog("Can only spawn in hand when you're a human or a monkey."))
where = "onfloor"
else if ( usr.get_active_hand() )
to_chat(usr, span_filter_adminlog("Your active hand is full. Spawning on floor."))
where = "onfloor"
if ( where == "inmarked" )
if ( !marked_datum )
to_chat(usr, span_filter_adminlog("You don't have any object marked. Abandoning spawn."))
return
else
if ( !istype(marked_datum,/atom) )
to_chat(usr, span_filter_adminlog("The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn."))
return
var/atom/target //Where the object will be spawned
switch ( where )
if ( "onfloor" )
switch (href_list["offset_type"])
if ("absolute")
target = locate(0 + X,0 + Y,0 + Z)
if ("relative")
target = locate(loc.x + X,loc.y + Y,loc.z + Z)
if ( "inmarked" )
target = marked_datum
if(target)
for (var/path in paths)
for (var/i = 0; i < number; i++)
if(path in typesof(/turf))
var/turf/O = target
var/turf/N = O.ChangeTurf(path)
if(N)
if(obj_name)
N.name = obj_name
else
var/atom/O = new path(target)
if(O)
O.set_dir(obj_dir)
if(obj_name)
O.name = obj_name
if(istype(O,/mob))
var/mob/M = O
M.real_name = obj_name
log_and_message_admins("created [number] [english_list(paths)]")
return
else if(href_list["spawn_panel"])
SSadmin_verbs.dynamic_invoke_verb(usr, /datum/admin_verb/spawn_panel)
//else if(href_list["admin_secrets_panel"])
//var/datum/admin_secret_category/AC = locate(href_list["admin_secrets_panel"]) in admin_secrets.categories
+7
View File
@@ -0,0 +1,7 @@
ADMIN_VERB(spawn_panel, R_SPAWN, "Spawn Panel", "Spawn Panel (TGUI).", ADMIN_CATEGORY_GAME)
var/datum/spawnpanel/panel = user.holder.spawn_panel
if(!panel)
panel = new()
user.holder.spawn_panel = panel
panel.tgui_interact(user.mob)
feedback_add_details("admin_verb","SP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+2
View File
@@ -40,6 +40,8 @@
show_verb_panel = FALSE
///Contains admin info. Null if client is not an admin.
var/datum/admins/holder = null
///Needs to implement InterceptClickOn(user,params,atom) proc
var/datum/click_intercept = null
var/buildmode = 0
///Contains the last message sent by this client - used to protect against copy-paste spamming.
@@ -31,10 +31,10 @@
var/max_item_count = 1
var/upgraded_capacity = FALSE
var/gulpsound = 'sound/vore/gulp.ogg'
var/datum/matter_synth/metal = null
var/datum/matter_synth/glass = null
var/datum/matter_synth/wood = null
var/datum/matter_synth/plastic = null
var/datum/matter_synth/metal/metal = null
var/datum/matter_synth/glass/glass = null
var/datum/matter_synth/wood/wood = null
var/datum/matter_synth/plastic/plastic = null
var/datum/matter_synth/water = null
var/digest_brute = 2
var/digest_burn = 3
@@ -7,10 +7,10 @@
icon_state = "decompiler"
//Metal, glass, wood, plastic.
var/datum/matter_synth/metal = null
var/datum/matter_synth/glass = null
var/datum/matter_synth/wood = null
var/datum/matter_synth/plastic = null
var/datum/matter_synth/metal/metal = null
var/datum/matter_synth/glass/glass = null
var/datum/matter_synth/wood/wood = null
var/datum/matter_synth/plastic/plastic = null
/obj/item/matter_decompiler/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob)
return
@@ -184,6 +184,85 @@
CHANNEL_EXPLORATION = 1
)
/obj/item/robot_module/proc/add_item_with_reagents(obj/item/stack/item_with_synth)
var/list/item_synths = list()
for(var/datum/matter_synth/matter_synth as anything in item_with_synth.synths)
var/found = FALSE
for(var/datum/matter_synth/synth as anything in synths)
if(matter_synth.type == synth.type)
item_synths += synth
break
if(!found)
var/datum/matter_synth/new_synth = new matter_synth.type(10000)
item_synths += new_synth
synths += new_synth
item_with_synth.synths = item_synths
/obj/item/robot_module/proc/add_item(atom/movable/new_item, mob/living/silicon/robot/robot)
if(istype(new_item, /obj/item/card/id))
if(robot.idcard)
modules -= robot.idcard
QDEL_NULL(robot.idcard)
robot.idcard = new_item
modules += new_item
new_item.forceMove(src)
robot.hud_used?.update_robot_modules_display()
if(istype(new_item, /obj/item/robotic_multibelt/materials))
var/obj/item/robotic_multibelt/materials/mat_belt = new_item
for(var/obj/item/stack as anything in mat_belt.cyborg_integrated_tools)
add_item_with_reagents(stack)
if(istype(new_item, /obj/item/stack))
add_item_with_reagents(new_item)
if(istype(new_item, /obj/item/matter_decompiler) || istype(new_item, /obj/item/dogborg/sleeper/compactor/decompiler))
var/obj/item/matter_decompiler/item_with_matter = new_item
if(item_with_matter.metal)
var/found = FALSE
for(var/datum/matter_synth/synth as anything in synths)
if(item_with_matter.metal.type == synth.type)
item_with_matter.metal = synth
found = TRUE
break
if(!found)
var/datum/matter_synth/metal = new /datum/matter_synth/metal(40000)
item_with_matter.metal = metal
LAZYADD(synths, metal)
if(item_with_matter.glass)
var/found = FALSE
for(var/datum/matter_synth/synth as anything in synths)
if(item_with_matter.glass.type == synth.type)
item_with_matter.glass = synth
found = TRUE
break
if(!found)
var/datum/matter_synth/glass = new /datum/matter_synth/glass(40000)
item_with_matter.glass = glass
LAZYADD(synths, glass)
if(item_with_matter.wood)
var/found = FALSE
for(var/datum/matter_synth/synth as anything in synths)
if(item_with_matter.wood.type == synth.type)
item_with_matter.wood = synth
found = TRUE
break
if(!found)
var/datum/matter_synth/wood = new /datum/matter_synth/wood(40000)
item_with_matter.wood = wood
LAZYADD(synths, wood)
if(item_with_matter.plastic)
var/found = FALSE
for(var/datum/matter_synth/synth as anything in synths)
if(item_with_matter.plastic.type == synth.type)
item_with_matter.plastic = synth
found = TRUE
break
if(!found)
var/datum/matter_synth/plastic = new /datum/matter_synth/plastic(40000)
item_with_matter.plastic = plastic
LAZYADD(synths, plastic)
// Cyborgs (non-drones), default loadout. This will be given to every module.
/obj/item/robot_module/robot/create_equipment(var/mob/living/silicon/robot/robot)
..()
+3
View File
@@ -269,6 +269,9 @@
///For storing what do_after's someone has, key = string, value = amount of interactions of that type happening.
var/list/do_afters
///Allows a datum to intercept all click calls this mob is the source of
var/datum/click_intercept
var/datum/focus //What receives our keyboard inputs. src by default
/// dict of custom stat tabs with data
var/list/list/misc_tabs = list()
-150
View File
@@ -1,150 +0,0 @@
<html>
<head>
<title>Create Object</title>
<style type="text/css">
body {
font-size: 9pt;
font-family: Verdana, sans-serif;
}
h1,
h2,
h3,
h4,
h5,
h6 {
color: #00f;
font-family: Georgia, Arial, sans-serif;
}
img {
border: 0px;
}
p.lic {
font-size: 6pt;
}
</style>
</head>
<body>
<form name="spawner" action="byond://?src=/* ref src */" method="get">
<input type="hidden" name="src" value="/* ref src */" />
/* hreftokenfield */
<input type="hidden" name="action" value="object_list" />
Type
<input
type="text"
name="filter"
value=""
onkeypress="submitFirst(event)"
style="width: 280px; height: 25"
/>
<input type="button" value="Search" onclick="updateSearch()" /><br />
Offset:
<input type="text" name="offset" value="x,y,z" style="width: 250px" />
A <input type="radio" name="offset_type" value="absolute" /> R
<input
type="radio"
name="offset_type"
value="relative"
checked="checked"
/><br />
Number:
<input type="text" name="object_count" value="1" style="width: 30px" />
Dir:
<input type="text" name="object_dir" value="2" style="width: 30px" />
Name:
<input
type="text"
name="object_name"
value=""
style="width: 180px"
/><br />
Where:
<select name="object_where" style="width: 320px">
<option value="onfloor">On floor below own mob</option>
<!-- <option value='inhand'>In own mob's hand</option> -->
<option value="inmarked">In marked object</option>
</select>
<br /><br />
Number of matches:
<input id="number" value="0" style="width: 180px" /><br />
<br /><br />
<div id="selector_hs">
<select name="object_list" id="object_list" multiple size="20"></select>
</div>
<br />
<input type="submit" value="spawn" />
</form>
<script language="JavaScript">
var old_search = "";
var object_list = document.spawner.object_list;
var object_list_container = document.getElementById("selector_hs");
// prettier-ignore
var object_paths = (null /* object types */);
var objects =
object_paths == null ? new Array() : object_paths.split(";");
document.spawner.filter.focus();
populateList(objects);
function populateList(from_list) {
var newOpts = "";
var i;
for (i in from_list) {
newOpts +=
'<option value="' +
from_list[i] +
'">' +
from_list[i] +
"</option>";
}
object_list_container.innerHTML =
'<select name="object_list" id="object_list" multiple size="20">' +
newOpts +
"</select>";
document.getElementById("number").value = from_list.length;
}
function updateSearch() {
if (old_search == document.spawner.filter.value) {
return false;
}
old_search = document.spawner.filter.value;
var filtered = new Array();
var i;
for (i in objects) {
if (objects[i].search(old_search) < 0) {
continue;
}
filtered.push(objects[i]);
}
populateList(filtered);
if (object_list.options.length)
object_list.options[0].selected = "true";
return true;
}
function submitFirst(event) {
if (!object_list.options.length) {
return false;
}
if (event.keyCode == 13 || event.which == 13) {
object_list.options[0].selected = "true";
}
}
</script>
</body>
</html>
@@ -11,33 +11,17 @@ export function exportChatSettings(): void {
const chatPages = store.get(chatPagesRecordAtom);
const settings = store.get(storedSettingsAtom);
const opts: SaveFilePickerOptions = {
id: `ss13-chatprefs-${Date.now()}`,
suggestedName: `ss13-chatsettings-${new Date().toJSON().slice(0, 10)}.json`,
types: [
{
description: 'SS13 file',
accept: { 'application/json': ['.json'] },
},
],
};
const exportObject = { ...settings, chatPages };
window
.showSaveFilePicker(opts)
.then((fileHandle) => {
fileHandle.createWritable().then((writableHandle) => {
writableHandle.write(JSON.stringify(exportObject));
writableHandle.close();
});
})
.catch((e) => {
// Log the error if the error has nothing to do with the user aborting the download
if (e.name !== 'AbortError') {
console.error(e);
}
});
const blob = new Blob([JSON.stringify(exportObject)], {
type: 'application/json',
});
Byond.saveBlob(
blob,
`ss13-chatsettings-${new Date().toJSON().slice(0, 10)}.json`,
'.json',
);
}
export function importChatSettings(settings: string | string[]): void {
@@ -0,0 +1,475 @@
import { storage } from 'common/storage';
import { useCallback, useEffect, useState } from 'react';
import {
Button,
DmIcon,
Input,
NoticeBox,
Section,
Stack,
VirtualList,
} from 'tgui-core/components';
import { useFuzzySearch } from 'tgui-core/fuzzysearch';
import { useBackend } from '../../backend';
import { CreateObjectSettings } from './CreateObjectSettings';
import { listNames, listTypes } from './constants';
import type {
AtomData,
CreateObjectProps,
SpawnPanelPreferences,
} from './types';
type SpawnPanelData = {
icon: string;
iconState: string;
selected_object?: string;
copied_type?: string;
preferences?: SpawnPanelPreferences;
};
interface SpawnPreferences {
hide_icons: boolean;
hide_mappings: boolean;
sort_by: string;
search_text: string;
search_by: string;
selected_atom?: string;
}
export function CreateObject(props: CreateObjectProps) {
const { act, data } = useBackend<SpawnPanelData>();
const { setAdvancedSettings, iconSettings, objList = { atoms: {} } } = props;
const [tooltipIcon, setTooltipIcon] = useState(false);
const [selectedObj, setSelectedObj] = useState<string | null>(null);
const [searchBy, setSearchBy] = useState(false);
const [sortBy, setSortBy] = useState(listTypes.Objects);
const [hideMapping, setHideMapping] = useState(false);
const [showIcons, setshowIcons] = useState(false);
const [showPreview, setshowPreview] = useState(false);
// flattening the object lists
const allObjects = objList.atoms;
const currentList = objList;
const currentType = allObjects[data.copied_type ?? '']?.type || 'Objects';
const getSearchString = useCallback(
(key: string) => {
const item = allObjects[key];
if (!item) return key;
return searchBy ? key : `${key} ${item.name || ''}`;
},
[searchBy, allObjects],
);
const { query, setQuery, results } = useFuzzySearch({
searchArray: Object.keys(allObjects),
matchStrategy: 'smart',
getSearchString,
});
const filteredResults = results.filter((obj) => {
const item = allObjects[obj];
if (!item) return false;
if (sortBy !== listTypes[item.type]) return false;
if (hideMapping && item.mapping) return false;
return true;
});
useEffect(() => {
if (data.selected_object) {
setSelectedObj(data.selected_object);
if (currentList[data.selected_object]) {
props.onIconSettingsChange?.({
icon: currentList[data.selected_object].icon,
iconState: currentList[data.selected_object].icon_state,
});
}
}
}, [data.selected_object]);
useEffect(() => {
if (data.copied_type) {
setSelectedObj(data.copied_type);
setQuery(data.copied_type);
const copiedAtom: AtomData = objList.atoms[data.copied_type];
setSortBy(listTypes[copiedAtom.type]);
setSearchBy(true);
const list = objList.atoms;
if (list[data.copied_type]) {
props.onIconSettingsChange?.({
icon: list[data.copied_type].icon,
iconState: list[data.copied_type].icon_state,
});
}
}
}, [data.copied_type]);
useEffect(() => {
const loadStoredValues = async () => {
const storedSearchText = await storage.get('spawnpanel-searchText');
const storedSearchBy = await storage.get('spawnpanel-searchBy');
const storedSortBy = await storage.get('spawnpanel-sortBy');
const storedHideMapping = await storage.get('spawnpanel-hideMapping');
const storedShowIcons = await storage.get('spawnpanel-showIcons');
const storedShowPreview = await storage.get('spawnpanel-showPreview');
const storedSelectedObj = await storage.get('spawnpanel-selectedObj');
if (storedSearchText) setQuery(storedSearchText);
if (storedSearchBy !== undefined) setSearchBy(storedSearchBy);
if (storedSortBy) setSortBy(storedSortBy);
if (storedHideMapping !== undefined) setHideMapping(storedHideMapping);
if (storedShowIcons !== undefined) setshowIcons(storedShowIcons);
if (storedShowPreview !== undefined) setshowPreview(storedShowPreview);
if (storedSelectedObj && allObjects[storedSelectedObj]) {
setSelectedObj(storedSelectedObj);
props.onIconSettingsChange?.({
icon: allObjects[storedSelectedObj].icon,
iconState: allObjects[storedSelectedObj].icon_state,
});
}
};
loadStoredValues();
}, []);
useEffect(() => {
setSelectedObj(null);
}, [currentType]);
useEffect(() => {
if (selectedObj !== null) {
storage.set('spawnpanel-selectedObj', selectedObj);
}
}, [selectedObj]);
useEffect(() => {
if (data.selected_object) {
setSelectedObj(data.selected_object);
} else if (data.copied_type) {
setSelectedObj(data.copied_type);
}
}, [data.selected_object, data.copied_type]);
useEffect(() => {
if (data.iconState !== undefined) {
props.onIconSettingsChange({
iconState: data.iconState,
});
}
}, [data.iconState]);
const sendUpdatedSettings = (
changedSettings: Partial<Record<string, unknown>> = {},
) => {
act('update-settings', changedSettings);
};
const updateSearchText = (value: string) => {
setQuery(value);
storage.set('spawnpanel-searchText', value);
};
const updateSearchBy = (value: boolean) => {
setSearchBy(value);
storage.set('spawnpanel-searchBy', value);
};
const updateSortBy = (value: string) => {
setSortBy(value);
storage.set('spawnpanel-sortBy', value);
};
const updateHideMapping = (value: boolean) => {
setHideMapping(value);
storage.set('spawnpanel-hideMapping', value);
};
const updateShowIcons = (value: boolean) => {
setshowIcons(value);
storage.set('spawnpanel-showIcons', value);
};
const updateShowPreview = (value: boolean) => {
setshowPreview(value);
storage.set('spawnpanel-showPreview', value);
};
const sendPreferences = (settings: Partial<SpawnPreferences>) => {
const parseOffset = (offsetStr: string): number[] => {
if (!offsetStr.trim()) return [0, 0, 0];
const parts = offsetStr.split(',').map((part) => {
return parseInt(part.trim(), 10);
});
while (parts.length < 3) {
parts.push(0);
}
return parts.slice(0, 3);
};
const prefsToSend = {
hide_icons: showIcons,
hide_mappings: hideMapping,
sort_by:
Object.keys(listTypes).find((key) => listTypes[key] === sortBy) ||
'Objects',
search_text: query,
search_by: searchBy ? 'type' : 'name',
...settings,
};
act('create-atom-action', prefsToSend);
};
const handleObjectSelect = (obj: string) => {
setSelectedObj(obj);
act('selected-atom-changed', {
newObj: obj,
});
if (allObjects[obj]) {
props.onIconSettingsChange?.({
icon: allObjects[obj].icon,
iconState: allObjects[obj].icon_state,
});
}
};
const handleResetIconState = () => {
props.onIconSettingsChange({
iconState: null,
icon: selectedObj ? allObjects[selectedObj]?.icon : null,
});
act('reset-icon-state');
};
return (
<Stack vertical fill>
<Stack.Item>
<Section>
<CreateObjectSettings
onCreateObject={sendPreferences}
setAdvancedSettings={setAdvancedSettings}
iconSettings={iconSettings}
/>
</Section>
</Stack.Item>
{showPreview && selectedObj && allObjects[selectedObj] && (
<Stack.Item>
<Section style={{ height: '6em' }}>
<Stack>
<Stack.Item>
<Button
width="5em"
height="4.8em"
mb="-3px"
color="transparent"
ml="1px"
style={{ alignContent: 'center' }}
>
<DmIcon
width="4em"
mt="2px"
icon={
iconSettings.icon ||
data.icon ||
allObjects[selectedObj].icon
}
icon_state={
iconSettings.iconState ||
data.iconState ||
allObjects[selectedObj].icon_state
}
/>
</Button>
</Stack.Item>
<Stack.Item
grow
style={{
maxHeight: '4.8em',
overflowY: 'auto',
}}
>
<Stack vertical>
<Stack.Item bold>{allObjects[selectedObj].name}</Stack.Item>
<Stack.Item
grow
italic
style={{ color: 'rgba(200, 200, 200, 0.7)' }}
>
{allObjects[selectedObj].description || 'no description'}
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Section>
</Stack.Item>
)}
<Stack.Item>
<Section>
<Stack vertical>
<Stack>
<Stack.Item>
<Button
icon={sortBy}
onClick={() => {
const types = Object.values(listTypes);
const currentIndex = types.indexOf(sortBy);
const nextIndex = (currentIndex + 1) % types.length;
updateSortBy(types[nextIndex]);
}}
tooltip={`Cycle the searching target (objects, mobs, turfs)`}
>
{
listNames[
Object.keys(listTypes).find(
(key) => listTypes[key] === sortBy,
) || 'Objects'
]
}
</Button>
</Stack.Item>
<Stack.Item>
<Button
icon={searchBy ? 'code' : 'font'}
onClick={() => {
updateSearchBy(!searchBy);
}}
tooltip={`Cycle the search method (by name, by type)`}
>
{searchBy ? 'By type' : 'By name'}
</Button>
</Stack.Item>
<Stack.Item>
<Button.Checkbox
onClick={() => {
updateHideMapping(!hideMapping);
}}
color={!hideMapping && 'good'}
checked={!hideMapping}
tooltip={`Toggle mapping objects visibility`}
>
Mapping
</Button.Checkbox>
</Stack.Item>
<Stack.Item>
<Button.Checkbox
onClick={() => {
updateShowIcons(!showIcons);
}}
color={showIcons && 'good'}
checked={showIcons}
tooltip={`Toggle preview icons on hovering`}
>
Icons
</Button.Checkbox>
</Stack.Item>
<Stack.Item>
<Button.Checkbox
onClick={() => {
updateShowPreview(!showPreview);
}}
color={showPreview && 'good'}
checked={showPreview}
tooltip={`Toggle the large object preview panel`}
>
Preview
</Button.Checkbox>
</Stack.Item>
<Stack.Item>
<Button
height="1.7em"
color="transparent"
icon="close"
onClick={() => setQuery('')}
/>
</Stack.Item>
</Stack>
<Stack.Item grow>
<Input
placeholder="Search here..."
value={query}
onChange={(value) => updateSearchText(value)}
fluid
/>
</Stack.Item>
</Stack>
</Section>
</Stack.Item>
<Stack.Item grow>
<Section fill scrollable={filteredResults.length !== 0}>
{query === '' ? (
<NoticeBox textAlign="center" color="blue" width="100%">
Begin typing to search...
</NoticeBox>
) : !filteredResults.length ? (
<NoticeBox textAlign="center" color="blue" width="100%">
Nothing found
</NoticeBox>
) : (
<VirtualList>
{filteredResults.map((obj, index) => (
<Button
key={index}
color="transparent"
tooltip={
(showIcons || tooltipIcon) &&
allObjects[obj] && (
<DmIcon
icon={allObjects[obj].icon}
icon_state={allObjects[obj].icon_state}
/>
)
}
tooltipPosition="top-start"
fluid
selected={selectedObj === obj}
style={{
backgroundColor:
selectedObj === obj
? 'rgba(160, 200, 255, 0.1)'
: undefined,
color: selectedObj === obj ? '#fff' : undefined,
}}
onDoubleClick={() => {
if (selectedObj) {
sendPreferences({ selected_atom: selectedObj });
}
}}
onClick={() => handleObjectSelect(obj)}
>
{searchBy ? (
obj
) : (
<>
{allObjects[obj]?.name}
<span
className="label label-info"
style={{
marginLeft: '0.5em',
color: 'rgba(200, 200, 200, 0.5)',
fontSize: '10px',
}}
>
{obj}
</span>
</>
)}
</Button>
))}
</VirtualList>
)}
</Section>
</Stack.Item>
</Stack>
);
}
@@ -0,0 +1,146 @@
import { useEffect } from 'react';
import { Button, Dropdown, Slider, Table } from 'tgui-core/components';
import { useBackend } from '../../backend';
import type { IconSettings } from './index';
type SpawnPanelData = {
icon: string;
iconState: string;
iconStates: string[];
selected_object?: string;
apply_icon_override?: boolean;
};
interface CreateObjectAdvancedSettingsProps {
iconSettings: IconSettings;
onIconSettingsChange: (settings: Partial<IconSettings>) => void;
}
export function CreateObjectAdvancedSettings(
props: CreateObjectAdvancedSettingsProps,
) {
const { act, data } = useBackend<SpawnPanelData>();
const { iconSettings, onIconSettingsChange } = props;
const sendUpdatedSettings = (
changedSettings: Partial<Record<string, unknown>> = {},
) => {
const currentSettings = {
selected_atom_icon: data.icon || iconSettings.icon,
selected_atom_icon_state: data.iconState || iconSettings.iconState,
atom_icon_size: iconSettings.iconSize,
...changedSettings,
};
act('update-settings', currentSettings);
};
useEffect(() => {
act('get-icon-states');
}, []);
const iconStateOptions = (
Array.isArray(data.iconStates) ? data.iconStates : []
).map((state) => ({
value: state,
displayText: state,
}));
return (
<Table>
<Table.Row className="candystripe" lineHeight="26px">
<Table.Cell pl={1} width="fit-content">
Icon:
</Table.Cell>
<Table.Cell>
<Button fluid onClick={() => act('select-new-DMI')}>
{data.icon || iconSettings.icon || 'Default'}
</Button>
</Table.Cell>
<Table.Cell pr={1} width="25px">
<Button
icon="arrow-rotate-right"
color="transparent"
onClick={() => {
act('reset-DMI-icon');
sendUpdatedSettings();
}}
/>
</Table.Cell>
</Table.Row>
<Table.Row className="candystripe" lineHeight="26px">
<Table.Cell pl={1}>Icon state:</Table.Cell>
<Table.Cell>
<Dropdown
options={iconStateOptions}
selected={data.iconState || iconSettings.iconState || 'Default'}
displayText={data.iconState || iconSettings.iconState || 'Default'}
onSelected={(value) => {
act('select-new-icon-state', {
new_state: value,
current_icon: data.icon || iconSettings.icon,
});
onIconSettingsChange({ iconState: value });
sendUpdatedSettings({ selected_atom_icon_state: value });
}}
width="100%"
/>
</Table.Cell>
<Table.Cell pr={1}>
<Button
icon="arrow-rotate-right"
color="transparent"
onClick={() => {
onIconSettingsChange({ iconState: null });
act('reset-icon-state');
}}
/>
</Table.Cell>
</Table.Row>
<Table.Row className="candystripe" lineHeight="26px">
<Table.Cell pl={1}>Explicitly set icon:</Table.Cell>
<Table.Cell>
<Button.Checkbox
checked={!!iconSettings.applyIcon}
onClick={() => {
const next = !iconSettings.applyIcon;
onIconSettingsChange({ applyIcon: next });
act('set-apply-icon-override', { value: next });
}}
>
Enabled
</Button.Checkbox>
</Table.Cell>
<Table.Cell pr={1} />
</Table.Row>
<Table.Row className="candystripe" lineHeight="26px">
<Table.Cell pl={1}>Icon scale:</Table.Cell>
<Table.Cell width="auto">
<Slider
minValue={25}
maxValue={500}
value={iconSettings.iconSize}
step={25}
lineHeight={1}
stepPixelSize={20}
onChange={(e, value) => {
onIconSettingsChange({ iconSize: value });
sendUpdatedSettings({ atom_icon_size: value });
}}
/>
</Table.Cell>
<Table.Cell pr={1}>
<Button
icon="arrow-rotate-right"
color="transparent"
onClick={() => {
onIconSettingsChange({ iconSize: 100 });
act('reset-icon-size');
sendUpdatedSettings({ atom_icon_size: 100 });
}}
/>
</Table.Cell>
</Table.Row>
</Table>
);
}
@@ -0,0 +1,492 @@
import { storage } from 'common/storage';
import { useEffect, useState } from 'react';
import {
Button,
Dropdown,
Icon,
Input,
NumberInput,
Slider,
Stack,
Table,
} from 'tgui-core/components';
import { useBackend } from '../../backend';
import {
directionNames,
directionRotation,
spawnLocationIcons,
spawnLocationOptions,
} from './constants';
import type { IconSettings } from './index';
export type SpawnPanelData = {
icon: string;
iconState: string;
iconStates: string[];
selected_object?: string;
precise_mode: string;
};
interface CreateObjectSettingsProps {
onCreateObject?: (obj: Record<string, unknown>) => void;
setAdvancedSettings: (value: boolean) => void;
iconSettings: IconSettings;
}
export function CreateObjectSettings(props: CreateObjectSettingsProps) {
const { onCreateObject, setAdvancedSettings, iconSettings } = props;
const { act, data } = useBackend<SpawnPanelData>();
const [amount, setAmount] = useState(1);
const [cordsType, setCordsType] = useState(0);
const [spawnLocation, setSpawnLocation] = useState('Current location');
const [direction, setDirection] = useState(0);
const [objectName, setObjectName] = useState('');
const [offset, setOffset] = useState('');
const dirValues = [1, 5, 4, 6, 2, 10, 8, 9];
const updateAmount = (value: number) => {
setAmount(value);
sendUpdatedSettings({ atom_amount: value });
};
const updateCordsType = (value: number) => {
setCordsType(value);
storage.set('spawnpanel-offset_type', value);
sendUpdatedSettings({
offset_type: value ? 'Absolute offset' : 'Relative offset',
});
};
const updateSpawnLocation = (value: string) => {
setSpawnLocation(value);
storage.set('spawnpanel-where_target_type', value);
sendUpdatedSettings({ where_target_type: value });
};
const updateDirection = (value: number) => {
setDirection(value);
storage.set('spawnpanel-direction', value);
sendUpdatedSettings({ atom_dir: dirValues[value] });
};
const updateObjectName = (value: string) => {
setObjectName(value);
storage.set('spawnpanel-atom_name', value);
sendUpdatedSettings({ atom_name: value });
if (isPreciseModeActive) {
sendUpdatedSettings();
}
};
const updateOffset = (value: string) => {
setOffset(value);
storage.set('spawnpanel-offset', value);
const parseOffset = (offsetStr: string): number[] => {
if (!offsetStr.trim()) return [0, 0, 0];
const parts = offsetStr.split(',').map((part) => {
return parseInt(part.trim(), 10);
});
while (parts.length < 3) {
parts.push(0);
}
return parts.slice(0, 3);
};
sendUpdatedSettings({ offset: parseOffset(value) });
};
const sendUpdatedSettings = (
changedSettings: Partial<Record<string, unknown>> = {},
) => {
const parseOffset = (offsetStr: string): number[] => {
if (!offsetStr.trim()) return [0, 0, 0];
const parts = offsetStr.split(',').map((part) => {
return parseInt(part.trim(), 10);
});
while (parts.length < 3) {
parts.push(0);
}
return parts.slice(0, 3);
};
const currentSettings = {
atom_amount: amount,
offset_type: cordsType ? 'Absolute offset' : 'Relative offset',
where_target_type: spawnLocation,
atom_dir: dirValues[direction],
offset: parseOffset(offset),
atom_name: objectName,
atom_icon_size: iconSettings.iconSize,
apply_icon_override: iconSettings.applyIcon ?? false,
...changedSettings,
};
act('update-settings', currentSettings);
};
const resetAdvancedSettings = () => {
const defaultAmount = 1;
const defaultCordsType = 0;
const defaultSpawnLocation = 'Current location';
const defaultDirection = 0;
const defaultObjectName = '';
const defaultOffset = '';
setAmount(defaultAmount);
setCordsType(defaultCordsType);
setSpawnLocation(defaultSpawnLocation);
setDirection(defaultDirection);
setObjectName(defaultObjectName);
setOffset(defaultOffset);
storage.set('spawnpanel-atom_amount', defaultAmount);
storage.set('spawnpanel-offset_type', defaultCordsType);
storage.set('spawnpanel-where_target_type', defaultSpawnLocation);
storage.set('spawnpanel-direction', defaultDirection);
storage.set('spawnpanel-atom_name', defaultObjectName);
storage.set('spawnpanel-offset', defaultOffset);
sendUpdatedSettings({
atom_amount: defaultAmount,
offset_type: defaultCordsType ? 'Absolute offset' : 'Relative offset',
where_target_type: defaultSpawnLocation,
atom_dir: dirValues[defaultDirection],
offset: defaultOffset,
atom_name: defaultObjectName,
});
};
useEffect(() => {
const loadStoredValues = async () => {
const storedCordsType = await storage.get('spawnpanel-offset_type');
const storedSpawnLocation = await storage.get(
'spawnpanel-where_target_type',
);
const storedDirection = await storage.get('spawnpanel-direction');
const storedObjectName = await storage.get('spawnpanel-atom_name');
const storedOffset = await storage.get('spawnpanel-offset');
if (storedCordsType !== undefined) setCordsType(storedCordsType);
if (storedSpawnLocation) setSpawnLocation(storedSpawnLocation);
if (storedDirection !== undefined) setDirection(storedDirection);
if (storedObjectName !== undefined) setObjectName(storedObjectName);
if (storedOffset !== undefined) setOffset(storedOffset);
};
loadStoredValues();
}, []);
const isTargetMode =
spawnLocation === 'Targeted location' ||
spawnLocation === 'Targeted location (droppod)' ||
spawnLocation === "In targeted mob's hand";
const isPreciseModeActive = data?.precise_mode === 'Target';
const isMarkModeActive = data?.precise_mode === 'Mark';
const isCopyModeActive = data?.precise_mode === 'Copy';
const isAnyPreciseModeActive = !(data?.precise_mode === 'Off');
const disablePreciseMode = () => {
if (isPreciseModeActive) {
act('toggle-precise-mode', {
newPreciseType: 'Off',
});
}
};
const handleSpawn = () => {
const parseOffset = (offsetStr: string): number[] => {
if (!offsetStr.trim()) return [0, 0, 0];
const parts = offsetStr.split(',').map((part) => {
return parseInt(part.trim(), 10);
});
while (parts.length < 3) {
parts.push(0);
}
return parts.slice(0, 3);
};
const currentSettings = {
atom_amount: amount,
offset_type: cordsType ? 'Absolute offset' : 'Relative offset',
where_target_type: spawnLocation,
atom_dir: dirValues[direction],
offset: parseOffset(offset),
atom_name: objectName,
atom_icon_size: iconSettings.iconSize,
selected_atom: data.selected_object,
apply_icon_override: iconSettings.applyIcon ?? false,
};
act('update-settings', currentSettings);
if (!isTargetMode) {
if (onCreateObject) {
onCreateObject(currentSettings);
} else {
act('create-atom-action', currentSettings);
}
} else {
if (isPreciseModeActive) {
act('toggle-precise-mode', { newPreciseType: 'Off' });
} else {
act('toggle-precise-mode', {
newPreciseType: 'Target',
where_target_type: spawnLocation,
});
}
}
};
useEffect(() => {
if (!isTargetMode && isPreciseModeActive) {
disablePreciseMode();
}
}, [spawnLocation]);
useEffect(() => {
if (isPreciseModeActive) {
sendUpdatedSettings();
}
}, [iconSettings.icon, iconSettings.iconState, iconSettings.iconSize]);
return (
<Stack fill vertical>
<Stack>
<Stack.Item grow>
<Table
style={{
paddingLeft: '5px',
}}
>
<Table.Row className="candystripe" lineHeight="26px">
<Table.Cell pl={1}>Amnt.:</Table.Cell>
<Table.Cell>
<Stack>
<Stack.Item>
<NumberInput
width="45px"
minValue={1}
maxValue={100}
step={1}
value={amount}
onChange={(value) => updateAmount(value)}
disabled={isAnyPreciseModeActive}
/>
</Stack.Item>
<Stack.Item>Dir:</Stack.Item>
<Stack.Item>
<Button
tooltip={directionNames[dirValues[direction]]}
tooltipPosition="top"
fontSize="14"
onClick={() => {
updateDirection((direction + 1) % dirValues.length);
}}
disabled={isAnyPreciseModeActive}
>
<Icon
name="arrow-up"
style={{
transform: `rotate(${directionRotation[dirValues[direction]]}deg)`,
}}
/>
</Button>
</Stack.Item>
<Stack.Item grow>
<Slider
tickWhileDragging
minValue={0}
maxValue={7}
step={1}
lineHeight={1}
stepPixelSize={25}
value={direction}
format={(value) => dirValues[value].toString()}
onChange={(e, value) => updateDirection(value)}
disabled={isAnyPreciseModeActive}
/>
</Stack.Item>
</Stack>
</Table.Cell>
</Table.Row>
<Table.Row className="candystripe" lineHeight="26px">
<Table.Cell pl={1}>Offset:</Table.Cell>
<Table.Cell width="1200px">
<Stack>
<Stack.Item>
<Button
icon={cordsType ? 'a' : 'r'}
fontSize="14"
onClick={() => {
const newCordsType = cordsType ? 0 : 1;
updateCordsType(newCordsType);
if (isPreciseModeActive) {
disablePreciseMode();
}
}}
tooltip={cordsType ? 'Absolute' : 'Relative'}
tooltipPosition="top"
disabled={
isTargetMode ||
spawnLocation === 'At a marked object' ||
spawnLocation === 'In the marked object' ||
isAnyPreciseModeActive
}
/>
</Stack.Item>
<Stack.Item grow>
<Input
placeholder="x, y, z"
value={offset}
onChange={(value: string) => updateOffset(value)}
width="100%"
disabled={
isTargetMode ||
spawnLocation === 'At a marked object' ||
spawnLocation === 'In the marked object' ||
isAnyPreciseModeActive
}
/>
</Stack.Item>
</Stack>
</Table.Cell>
</Table.Row>
<Table.Row className="candystripe" lineHeight="26px">
<Table.Cell pl={1} width="80px">
Name:
</Table.Cell>
<Table.Cell>
<Input
onChange={(value: string) => updateObjectName(value)}
value={objectName}
width="100%"
placeholder="leave empty for initial"
disabled={isAnyPreciseModeActive}
/>
</Table.Cell>
</Table.Row>
</Table>
</Stack.Item>
<Stack.Item grow>
<Stack fill>
<Stack.Item>
<Stack vertical>
<Stack.Item>
<Button
icon="gear"
onClick={() => setAdvancedSettings(true)}
style={{
height: '22px',
width: '22px',
lineHeight: '22px',
}}
tooltip="Advanced settings"
tooltipPosition="top"
disabled={isAnyPreciseModeActive}
/>
</Stack.Item>
<Stack.Item>
<Button
icon="trash"
style={{
height: '22px',
width: '22px',
lineHeight: '22px',
}}
onClick={() => resetAdvancedSettings()}
tooltip="Reset advanced settings"
tooltipPosition="top"
disabled={isAnyPreciseModeActive}
/>
</Stack.Item>
<Stack.Item>
<Button
style={{
height: '22px',
width: '22px',
lineHeight: '22px',
}}
icon={
spawnLocation === 'At a marked object' ||
spawnLocation === 'In the marked object'
? 'thumbtack'
: 'eye-dropper'
}
onClick={() => {
act('toggle-precise-mode', {
newPreciseType:
isMarkModeActive || isCopyModeActive
? 'Off'
: spawnLocation === 'At a marked object' ||
spawnLocation === 'In the marked object'
? 'Mark'
: 'Copy',
});
}}
selected={isMarkModeActive || isCopyModeActive}
tooltip={
spawnLocation === 'At a marked object' ||
spawnLocation === 'In the marked object'
? 'Mark atom'
: 'Copy atom path'
}
tooltipPosition="top"
disabled={isAnyPreciseModeActive && !isMarkModeActive}
/>
</Stack.Item>
</Stack>
</Stack.Item>
<Stack.Item grow>
<Stack vertical fill>
<Stack.Item grow>
<Button
onClick={handleSpawn}
style={{
width: '100%',
height: '100%',
textAlign: 'center',
fontSize: '20px',
alignContent: 'center',
}}
icon={spawnLocationIcons[spawnLocation]}
selected={isTargetMode && isPreciseModeActive}
>
SPAWN
</Button>
</Stack.Item>
<Stack.Item>
<Dropdown
options={spawnLocationOptions}
onSelected={(value) => {
if (data?.precise_mode && data.precise_mode !== 'Off') {
act('toggle-precise-mode', {
newPreciseType: 'Off',
});
}
updateSpawnLocation(value);
}}
selected={spawnLocation}
/>
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Stack>
);
}
@@ -0,0 +1,55 @@
export const spawnLocationOptions = [
'Current location',
'Current location (droppod)',
"In own mob's hand",
'At a marked object',
'In the marked object',
'Targeted location',
'Targeted location (droppod)',
"In targeted mob's hand",
];
export const listTypes = {
Objects: 'cube',
Turfs: 'map',
Mobs: 'cat',
};
export const listNames = {
Objects: 'Search objects',
Turfs: 'Search turfs',
Mobs: 'Search mobs',
};
export const spawnLocationIcons = {
'Current location': 'map-marker',
'Current location (droppod)': 'parachute-box',
"In own mob's hand": 'hand-holding',
'At a marked object': 'floppy-disk',
'In the marked object': 'floppy-disk',
'Targeted location': 'crosshairs',
'Targeted location (droppod)': 'crosshairs',
"In targeted mob's hand": 'crosshairs',
};
export const directionRotation = {
1: 0,
5: 45,
4: 90,
6: 135,
2: 180,
10: 225,
8: 270,
9: 315,
};
export const directionNames = {
1: 'NORTH',
2: 'SOUTH',
4: 'EAST',
8: 'WEST',
5: 'NORTHEAST',
6: 'SOUTHEAST',
9: 'NORTHWEST',
10: 'SOUTHWEST',
};
@@ -0,0 +1,91 @@
import { useEffect, useState } from 'react';
import { Button, Modal, Section, Stack } from 'tgui-core/components';
import { fetchRetry } from 'tgui-core/http';
import { resolveAsset } from '../../assets';
import { Window } from '../../layouts';
import { logger } from '../../logging';
import { CreateObject } from './CreateObject';
import { CreateObjectAdvancedSettings } from './CreateObjectAdvancedSettings';
import type { CreateObjectData } from './types';
export interface IconSettings {
icon: string | null;
iconState: string | null;
iconSize: number;
applyIcon?: boolean;
}
export function SpawnPanel() {
const [data, setData] = useState<CreateObjectData | undefined>();
const [advancedSettings, setAdvancedSettings] = useState(false);
const [iconSettings, setIconSettings] = useState<IconSettings>({
icon: null,
iconState: null,
iconSize: 100,
applyIcon: false,
});
useEffect(() => {
fetchRetry(resolveAsset('spawnpanel_atom_data.json'))
.then((response) => response.json())
.then(setData)
.catch((error) => {
logger.log(
'Failed to fetch spawnpanel_atom_data.json',
JSON.stringify(error),
);
});
}, []);
const handleIconSettingsChange = (newSettings: Partial<IconSettings>) => {
setIconSettings((current) => ({
...current,
...newSettings,
}));
};
return (
<Window height={550} title="Spawn Panel" width={500} theme="admin">
<Window.Content>
{advancedSettings && (
<Modal
style={{
padding: '6px',
width: '30em',
marginTop: '-15em',
}}
>
<Section
title="Advanced settings"
buttons={
<Button
color="transparent"
icon="close"
onClick={() => setAdvancedSettings(false)}
/>
}
>
<CreateObjectAdvancedSettings
iconSettings={iconSettings}
onIconSettingsChange={handleIconSettingsChange}
/>
</Section>
</Modal>
)}
<Stack vertical fill>
<Stack.Item grow>
{data && (
<CreateObject
objList={data}
setAdvancedSettings={setAdvancedSettings}
iconSettings={iconSettings}
onIconSettingsChange={handleIconSettingsChange}
/>
)}
</Stack.Item>
</Stack>
</Window.Content>
</Window>
);
}
@@ -0,0 +1,53 @@
import type { BooleanLike } from 'tgui-core/react';
export interface SpawnPreferences {
hide_icons: boolean;
hide_mappings: boolean;
sort_by: string;
search_text: string;
search_by: string;
where_dropdown_value: string;
offset_type: string;
offset: string;
object_count: number;
dir: number;
object_name: string;
}
export interface SpawnPanelPreferences {
hide_icons: boolean;
hide_mappings: boolean;
sort_by: string;
search_text: string;
search_by: string;
}
export interface AtomData {
icon: string;
icon_state: string;
name: string;
description?: string;
mapping: BooleanLike;
type: 'Objects' | 'Turfs' | 'Mobs';
}
export interface CreateObjectData {
atoms: Record<string, AtomData>;
}
export interface CreateObjectProps {
objList: CreateObjectData;
setAdvancedSettings: (value: boolean) => void;
iconSettings: {
icon: string | null;
iconState: string | null;
iconSize: number;
};
onIconSettingsChange: (
settings: Partial<{
icon: string | null;
iconState: string | null;
iconSize: number;
}>,
) => void;
}
+5 -3
View File
@@ -2181,9 +2181,6 @@
#include "code\modules\admin\admin_verbs.dm"
#include "code\modules\admin\banjob.dm"
#include "code\modules\admin\ckey_vr.dm"
#include "code\modules\admin\create_mob.dm"
#include "code\modules\admin\create_object.dm"
#include "code\modules\admin\create_turf.dm"
#include "code\modules\admin\fix_player_notes_listing.dm"
#include "code\modules\admin\health_scan.dm"
#include "code\modules\admin\holder2.dm"
@@ -2204,8 +2201,13 @@
#include "code\modules\admin\callproc\callproc.dm"
#include "code\modules\admin\DB ban\ban_panle_ui.dm"
#include "code\modules\admin\DB ban\functions.dm"
#include "code\modules\admin\spawn_panel\admin_hotkeys.dm"
#include "code\modules\admin\spawn_panel\assets.dm"
#include "code\modules\admin\spawn_panel\spawn_handling.dm"
#include "code\modules\admin\spawn_panel\spawn_panel.dm"
#include "code\modules\admin\verb_datums\_admin_verb_datum.dm"
#include "code\modules\admin\verbs\adminfun.dm"
#include "code\modules\admin\verbs\admingame.dm"
#include "code\modules\admin\verbs\adminjump.dm"
#include "code\modules\admin\verbs\adminpm.dm"
#include "code\modules\admin\verbs\adminsay.dm"