diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm
index d4ab7b5648..a8b4c93764 100644
--- a/code/modules/asset_cache/asset_list_items.dm
+++ b/code/modules/asset_cache/asset_list_items.dm
@@ -388,3 +388,9 @@
Insert("polycrystal", 'icons/obj/telescience.dmi', "polycrystal")
..()
+/datum/asset/spritesheet/mafia
+ name = "mafia"
+
+/datum/asset/spritesheet/mafia/register()
+ InsertAll("", 'icons/obj/mafia.dmi')
+ ..()
diff --git a/code/modules/mafia/_defines.dm b/code/modules/mafia/_defines.dm
index b862ce63c5..3833d5aefe 100644
--- a/code/modules/mafia/_defines.dm
+++ b/code/modules/mafia/_defines.dm
@@ -32,6 +32,8 @@
GLOBAL_LIST_EMPTY(mafia_signup)
//the current global mafia game running.
GLOBAL_VAR(mafia_game)
+/// list of ghosts who want to play mafia that have since disconnected. They are kept in the lobby, but not counted for starting a game.
+GLOBAL_LIST_EMPTY(mafia_bad_signup)
GLOBAL_LIST_INIT(mafia_setups,generate_mafia_setups())
diff --git a/code/modules/mafia/controller.dm b/code/modules/mafia/controller.dm
index 172f9dca50..8c4006ef90 100644
--- a/code/modules/mafia/controller.dm
+++ b/code/modules/mafia/controller.dm
@@ -6,6 +6,8 @@
* It is first created when the first ghost signs up to play.
*/
/datum/mafia_controller
+ ///list of observers that should get game updates.
+ var/list/spectators = list()
///all roles in the game, dead or alive. check their game status if you only want living or dead.
var/list/all_roles = list()
///exists to speed up role retrieval, it's a dict. player_role_lookup[player ckey] will give you the role they play
@@ -48,7 +50,8 @@
///group voting on one person, like putting people to trial or choosing who to kill as mafia
var/list/votes = list()
- ///and these (judgement_innocent_votes and judgement_guilty_votes) are the judgement phase votes, aka people sorting themselves into guilty and innocent lists. whichever has more wins!
+ ///and these (judgement_innocent_votes, judgement_abstain_votes and judgement_guilty_votes) are the judgement phase votes, aka people sorting themselves into guilty and innocent, and "eh, i don't really care" lists. whichever has more inno or guilty wins!
+ var/list/judgement_abstain_votes = list()
var/list/judgement_innocent_votes = list()
var/list/judgement_guilty_votes = list()
///current role on trial for the judgement phase, will die if guilty is greater than innocent
@@ -129,7 +132,7 @@
var/team_suffix = team ? "([uppertext(team)] CHAT)" : ""
for(var/M in GLOB.dead_mob_list)
var/mob/spectator = M
- if(spectator.ckey in GLOB.mafia_signup || player_role_lookup[spectator.mind.current] != null) //was in current game, or is signed up
+ if(spectator.ckey in spectators) //was in current game, or spectatin' (won't send to living)
var/link = FOLLOW_LINK(M, town_center_landmark)
to_chat(M, "[link] MAFIA: [msg] [team_suffix]")
@@ -190,8 +193,19 @@
*/
/datum/mafia_controller/proc/check_trial(verbose = TRUE)
var/datum/mafia_role/loser = get_vote_winner("Day")//, majority_of_town = TRUE)
+ var/loser_votes = get_vote_count(loser,"Day")
if(loser)
+ // if(loser_votes > 12)
+ // loser.body.client?.give_award(/datum/award/achievement/mafia/universally_hated, loser.body)
send_message("[loser.body.real_name] wins the day vote, Listen to their defense and vote \"INNOCENT\" or \"GUILTY\"!")
+ //refresh the lists
+ judgement_abstain_votes = list()
+ judgement_innocent_votes = list()
+ judgement_guilty_votes = list()
+ for(var/i in all_roles)
+ var/datum/mafia_role/abstainee = i
+ if(abstainee.game_status == MAFIA_ALIVE && abstainee != loser)
+ judgement_abstain_votes += abstainee
on_trial = loser
on_trial.body.forceMove(get_turf(town_center_landmark))
phase = MAFIA_PHASE_JUDGEMENT
@@ -215,8 +229,11 @@
for(var/i in judgement_innocent_votes)
var/datum/mafia_role/role = i
send_message("[role.body.real_name] voted innocent.")
- for(var/ii in judgement_guilty_votes)
+ for(var/ii in judgement_abstain_votes)
var/datum/mafia_role/role = ii
+ send_message("[role.body.real_name] abstained.")
+ for(var/iii in judgement_guilty_votes)
+ var/datum/mafia_role/role = iii
send_message("[role.body.real_name] voted guilty.")
if(judgement_guilty_votes.len > judgement_innocent_votes.len) //strictly need majority guilty to lynch
send_message("Guilty wins majority, [on_trial.body.real_name] has been lynched.")
@@ -225,9 +242,6 @@
else
send_message("Innocent wins majority, [on_trial.body.real_name] has been spared.")
on_trial.body.forceMove(get_turf(on_trial.assigned_landmark))
- //by now clowns should have killed someone in guilty list, clear this out
- judgement_innocent_votes = list()
- judgement_guilty_votes = list()
on_trial = null
//day votes are already cleared, so this will skip the trial and check victory/lockdown/whatever else
next_phase_timer = addtimer(CALLBACK(src, .proc/check_trial, FALSE),judgement_lynch_period,TIMER_STOPPABLE)// small pause to see the guy dead, no verbosity since we already did this
@@ -320,10 +334,8 @@
/**
* Cleans up the game, resetting variables back to the beginning and removing the map with the generator.
*/
-/datum/mafia_controller/proc/end_game()
-
+/datum/mafia_controller/proc/end_game(
map_deleter.generate() //remove the map, it will be loaded at the start of the next one
-
QDEL_LIST(all_roles)
turn = 0
votes = list()
@@ -481,7 +493,7 @@
if(phase != MAFIA_PHASE_VOTING)
return
var/v = get_vote_count(player_role_lookup[source],"Day")
- var/mutable_appearance/MA = mutable_appearance('icons/obj/mafia.dmi',"vote_[v]")
+ var/mutable_appearance/MA = mutable_appearance('icons/obj/mafia.dmi',"vote_[v > 12 ? "over_12" : v]")
overlay_list += MA
/**
@@ -528,13 +540,26 @@
.["judgement_phase"] = FALSE
var/datum/mafia_role/user_role = player_role_lookup[user]
if(user_role)
- .["roleinfo"] = list("role" = user_role.name,"desc" = user_role.desc, "action_log" = user_role.role_notes)
+ .["roleinfo"] = list("role" = user_role.name,"desc" = user_role.desc, "action_log" = user_role.role_notes, "hud_icon" = user_role.hud_icon, "revealed_icon" = user_role.revealed_icon)
var/actions = list()
for(var/action in user_role.actions)
if(user_role.validate_action_target(src,action,null))
actions += action
.["actions"] = actions
.["role_theme"] = user_role.special_theme
+ else
+ var/list/lobby_data = list()
+ for(var/key in GLOB.mafia_signup + GLOB.mafia_bad_signup)
+ var/list/lobby_member = list()
+ lobby_member["name"] = key
+ lobby_member["status"] = "Ready"
+ if(key in GLOB.mafia_bad_signup)
+ lobby_member["status"] = "Disconnected"
+ lobby_member["spectating"] = "Ghost"
+ if(key in spectators)
+ lobby_member["spectating"] = "Spectator"
+ lobby_data += list(lobby_member)
+ .["lobbydata"] = lobby_data
var/list/player_data = list()
for(var/datum/mafia_role/R in all_roles)
var/list/player_info = list()
@@ -561,6 +586,11 @@
//Not sure on this, should this info be visible
.["all_roles"] = current_setup_text
+/datum/mafia_controller/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/spritesheet/mafia),
+ )
+
/datum/mafia_controller/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
@@ -606,7 +636,31 @@
helper = role
break
helper.show_help(usr)
- if(!user_role || user_role.game_status == MAFIA_DEAD)//ghosts, dead people?
+ if(!user_role)//just the dead
+ var/client/C = ui.user.client
+ switch(action)
+ if("mf_signup")
+ if(!SSticker.HasRoundStarted())
+ to_chat(usr, "Wait for the round to start.")
+ return
+ if(GLOB.mafia_signup[C.ckey])
+ GLOB.mafia_signup -= C.ckey
+ to_chat(usr, "You unregister from Mafia.")
+ return
+ else
+ GLOB.mafia_signup[C.ckey] = C
+ to_chat(usr, "You sign up for Mafia.")
+ if(phase == MAFIA_PHASE_SETUP)
+ check_signups()
+ try_autostart()
+ if("mf_spectate")
+ if(C.ckey in spectators)
+ to_chat(usr, "You will no longer get messages from the game.")
+ spectators -= C.ckey
+ else
+ to_chat(usr, "You will now get messages from the game.")
+ spectators += C.ckey
+ if(user_role.game_status == MAFIA_DEAD)
return
var/self_voting = user_role == on_trial ? TRUE : FALSE //used to block people from voting themselves innocent or guilty
//User actions
@@ -637,20 +691,29 @@
return
user_role.handle_action(src,params["atype"],target)
return TRUE
- if("vote_innocent")
- if(phase != MAFIA_PHASE_JUDGEMENT && !self_voting)
- return
- to_chat(user_role.body,"Your vote on [on_trial.body.real_name] submitted as INNOCENT!")
- judgement_innocent_votes -= user_role//no double voting
- judgement_guilty_votes -= user_role//no radical centrism
- judgement_innocent_votes += user_role
- if("vote_guilty")
- if(phase != MAFIA_PHASE_JUDGEMENT && !self_voting)
- return
- to_chat(user_role.body,"Your vote on [on_trial.body.real_name] submitted as GUILTY!")
- judgement_innocent_votes -= user_role//no radical centrism
- judgement_guilty_votes -= user_role//no double voting
- judgement_guilty_votes += user_role
+ if(user_role != on_trial)
+ switch(action)
+ if("vote_abstain")
+ if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_abstain_votes))
+ return
+ to_chat(user_role.body,"You have decided to abstain.")
+ judgement_innocent_votes -= user_role
+ judgement_guilty_votes -= user_role
+ judgement_abstain_votes += user_role
+ if("vote_innocent")
+ if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_innocent_votes))
+ return
+ to_chat(user_role.body,"Your vote on [on_trial.body.real_name] submitted as INNOCENT!")
+ judgement_abstain_votes -= user_role//no fakers, and...
+ judgement_guilty_votes -= user_role//no radical centrism
+ judgement_innocent_votes += user_role
+ if("vote_guilty")
+ if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_guilty_votes))
+ return
+ to_chat(user_role.body,"Your vote on [on_trial.body.real_name] submitted as GUILTY!")
+ judgement_abstain_votes -= user_role//no fakers, and...
+ judgement_innocent_votes -= user_role//no radical centrism
+ judgement_guilty_votes += user_role
/datum/mafia_controller/ui_state(mob/user)
return GLOB.always_state
@@ -699,7 +762,7 @@
//cuts invalid players from signups (disconnected/not a ghost)
var/list/possible_keys = list()
for(var/key in GLOB.mafia_signup)
- if(GLOB.directory[key] && GLOB.directory[key] == GLOB.mafia_signup[key])
+ if(GLOB.directory[key])
var/client/C = GLOB.directory[key]
if(isobserver(C.mob))
possible_keys += key
@@ -739,6 +802,25 @@
if(GLOB.mafia_signup.len >= min_players)//enough people to try and make something
basic_setup()
+/**
+ * Filters inactive player into a different list until they reconnect, and removes players who are no longer ghosts.
+ *
+ * If a disconnected player gets a non-ghost mob and reconnects, they will be first put back into mafia_signup then filtered by that.
+ */
+/datum/mafia_controller/proc/check_signups()
+ for(var/bad_key in GLOB.mafia_bad_signup)
+ if(GLOB.directory[bad_key])//they have reconnected if we can search their key and get a client
+ GLOB.mafia_bad_signup -= bad_key
+ GLOB.mafia_signup += bad_key
+ for(var/key in GLOB.mafia_signup)
+ var/client/C = GLOB.directory[key]
+ if(!C)//vice versa but in a variable we use later
+ GLOB.mafia_signup -= key
+ GLOB.mafia_bad_signup += key
+ if(!isobserver(C.mob))
+ //they are back to playing the game, remove them from the signups
+ GLOB.mafia_signup -= key
+
/datum/action/innate/mafia_panel
name = "Mafia Panel"
desc = "Use this to play."
diff --git a/code/modules/mafia/roles.dm b/code/modules/mafia/roles.dm
index 394d121005..914cbb0bd3 100644
--- a/code/modules/mafia/roles.dm
+++ b/code/modules/mafia/roles.dm
@@ -19,7 +19,12 @@
var/game_status = MAFIA_ALIVE
- var/special_theme //set this to something cool for antagonists and their window will look different
+ ///icon state in the mafia dmi of the hud of the role, used in the mafia ui
+ var/hud_icon = "hudassistant"
+ ///icon state in the mafia dmi of the hud of the role, used in the mafia ui
+ var/revealed_icon = "assistant"
+ ///set this to something cool for antagonists and their window will look different
+ var/special_theme
var/list/role_notes = list()
@@ -34,6 +39,8 @@
body.death()
if(lynch)
reveal_role(game, verbose = TRUE)
+ if(!(player_key in game.spectators)) //people who played will want to see the end of the game more often than not
+ game.spectators += player_key
return TRUE
/datum/mafia_role/Destroy(force, ...)
@@ -108,6 +115,9 @@
desc = "You can investigate a single person each night to learn their team."
revealed_outfit = /datum/outfit/mafia/detective
+ hud_icon = "huddetective"
+ revealed_icon = "detective"
+
targeted_actions = list("Investigate")
var/datum/mafia_role/current_investigation
@@ -152,7 +162,8 @@
name = "Medical Doctor"
desc = "You can protect a single person each night from killing."
revealed_outfit = /datum/outfit/mafia/md // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these)
-
+ hud_icon = "hudmedicaldoctor"
+ revealed_icon = "medicaldoctor"
targeted_actions = list("Protect")
var/datum/mafia_role/current_protected
@@ -194,7 +205,8 @@
name = "Chaplain"
desc = "You can communicate with spirits of the dead each night to discover dead crewmember roles."
revealed_outfit = /datum/outfit/mafia/chaplain
-
+ hud_icon = "hudchaplain"
+ revealed_icon = "chaplain"
targeted_actions = list("Pray")
var/current_target
@@ -222,8 +234,9 @@
/datum/mafia_role/lawyer
name = "Lawyer"
desc = "You can choose a person during the day to provide extensive legal advice to during the night, preventing night actions."
-
revealed_outfit = /datum/outfit/mafia/lawyer
+ hud_icon = "hudlawyer"
+ revealed_icon = "lawyer"
targeted_actions = list("Advise")
var/datum/mafia_role/current_target
@@ -278,6 +291,9 @@
desc = "You can visit someone ONCE PER GAME to reveal their true role in the morning!"
revealed_outfit = /datum/outfit/mafia/psychologist
+ hud_icon = "hudpsychologist"
+ revealed_icon = "psychologist"
+
targeted_actions = list("Reveal")
var/datum/mafia_role/current_target
var/can_use = TRUE
@@ -313,6 +329,8 @@
desc = "You're a member of the changeling hive. Use ':j' talk prefix to talk to your fellow lings."
team = MAFIA_TEAM_MAFIA
revealed_outfit = /datum/outfit/mafia/changeling
+ hud_icon = "hudchangeling"
+ revealed_icon = "changeling"
special_theme = "syndicate"
win_condition = "become majority over the town and no solo killing role can stop them."
@@ -332,7 +350,10 @@
team = MAFIA_TEAM_SOLO
targeted_actions = list("Night Kill")
revealed_outfit = /datum/outfit/mafia/traitor
- special_theme = "syndicate"
+
+ hud_icon = "hudtraitor"
+ revealed_icon = "traitor"
+ special_theme = "neutral"
var/datum/mafia_role/current_victim
@@ -384,6 +405,9 @@
var/protection_status = FUGITIVE_NOT_PRESERVING
solo_counts_as_town = TRUE //should not count towards mafia victory, they should have the option to work with town
revealed_outfit = /datum/outfit/mafia/fugitive
+ special_theme = "neutral"
+ hud_icon = "hudfugitive"
+ revealed_icon = "fugitive"
/datum/mafia_role/fugitive/New(datum/mafia_controller/game)
. = ..()
@@ -434,7 +458,9 @@
win_condition = "lynch their obsession."
team = MAFIA_TEAM_SOLO
revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these)
-
+ special_theme = "neutral"
+ hud_icon = "hudobsessed"
+ revealed_icon = "obsessed"
solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective!
var/datum/mafia_role/obsession
var/lynched_target = FALSE
@@ -470,10 +496,14 @@
/datum/mafia_role/clown
name = "Clown"
- desc = "If you are lynched you take down one of your voters with you and win. HONK!"
+ desc = "If you are lynched you take down one of your voters (guilty or abstain) with you and win. HONK!"
win_condition = "get themselves lynched!"
revealed_outfit = /datum/outfit/mafia/clown
+ solo_counts_as_town = TRUE
team = MAFIA_TEAM_SOLO
+ special_theme = "neutral"
+ hud_icon = "hudclown"
+ revealed_icon = "clown"
/datum/mafia_role/clown/New(datum/mafia_controller/game)
. = ..()
@@ -481,7 +511,7 @@
/datum/mafia_role/clown/proc/prank(datum/source,datum/mafia_controller/game,lynch)
if(lynch)
- var/datum/mafia_role/victim = pick(game.judgement_guilty_votes)
+ var/datum/mafia_role/victim = pick(game.judgement_guilty_votes + game.judgement_abstain_votes)
game.send_message("[body.real_name] WAS A CLOWN! HONK! They take down [victim.body.real_name] with their last prank.")
game.send_message("!! CLOWN VICTORY !!")
victim.kill(game,FALSE)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index bb39639ec1..a0df1ee938 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -905,6 +905,22 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
else
to_chat(usr, "Can't become a pAI candidate while not dead!")
+/mob/dead/observer/verb/mafia_game_signup()
+ set category = "Ghost"
+ set name = "Signup for Mafia"
+ set desc = "Sign up for a game of Mafia to pass the time while dead."
+ mafia_signup()
+/mob/dead/observer/proc/mafia_signup()
+ if(!client)
+ return
+ if(!isobserver(src))
+ to_chat(usr, "You must be a ghost to join mafia!")
+ return
+ var/datum/mafia_controller/game = GLOB.mafia_game //this needs to change if you want multiple mafia games up at once.
+ if(!game)
+ game = create_mafia_game("mafia")
+ game.ui_interact(usr)
+
/mob/dead/observer/CtrlShiftClick(mob/user)
if(isobserver(user) && check_rights(R_SPAWN))
change_mob_type( /mob/living/carbon/human , null, null, TRUE) //always delmob, ghosts shouldn't be left lingering
diff --git a/icons/obj/mafia.dmi b/icons/obj/mafia.dmi
index fc0426a19f..c44b80aba1 100644
Binary files a/icons/obj/mafia.dmi and b/icons/obj/mafia.dmi differ
diff --git a/tgui/packages/tgui/assets/bg-neutral.svg b/tgui/packages/tgui/assets/bg-neutral.svg
new file mode 100644
index 0000000000..1c397616e8
--- /dev/null
+++ b/tgui/packages/tgui/assets/bg-neutral.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js
index e4ec6d285d..69092ce00f 100644
--- a/tgui/packages/tgui/index.js
+++ b/tgui/packages/tgui/index.js
@@ -22,6 +22,7 @@ import './styles/themes/abductor.scss';
import './styles/themes/cardtable.scss';
import './styles/themes/hackerman.scss';
import './styles/themes/malfunction.scss';
+import './styles/themes/neutral.scss';
import './styles/themes/ntos.scss';
import './styles/themes/paper.scss';
import './styles/themes/retro.scss';
diff --git a/tgui/packages/tgui/interfaces/MafiaPanel.js b/tgui/packages/tgui/interfaces/MafiaPanel.js
index e3c2c893ec..3616781fc5 100644
--- a/tgui/packages/tgui/interfaces/MafiaPanel.js
+++ b/tgui/packages/tgui/interfaces/MafiaPanel.js
@@ -1,11 +1,14 @@
+import { classes } from 'common/react';
import { Fragment } from 'inferno';
+import { multiline } from 'common/string';
import { useBackend } from '../backend';
-import { Box, Button, Flex, LabeledList, Section, TimeDisplay } from '../components';
+import { Box, Button, Collapsible, Flex, NoticeBox, Section, TimeDisplay, Tooltip } from '../components';
import { Window } from '../layouts';
export const MafiaPanel = (props, context) => {
const { act, data } = useBackend(context);
const {
+ lobbydata,
players,
actions,
phase,
@@ -16,29 +19,124 @@ export const MafiaPanel = (props, context) => {
timeleft,
all_roles,
} = data;
+ const playerAddedHeight = roleinfo ? players.length * 30 : 7;
+ const readyGhosts = lobbydata ? lobbydata.filter(
+ player => player.status === "Ready") : null;
return (
-
-
- {!!roleinfo && (
-
-
-
+ width={650} // 414 or 415 / 444 or 445
+ height={293 + playerAddedHeight}>
+
+ {!roleinfo && (
+
+
+ }>
+
+
+ The lobby currently has {readyGhosts.length}
+ /12 valid players signed up.
+
+
+ {!!lobbydata && lobbydata.map(lobbyist => (
+
+
+
+ {lobbyist.name}
+
+
+ STATUS:
+
+
+
+
+ {lobbyist.status} {lobbyist.spectating}
+
+
+
+
+
+ ))}
+
+
+
+ )}
+ {!!roleinfo && (
+
- You are a {roleinfo.role}
+ {!!admin_controls && (
+
+ )}
-
+ }>
+
+
+ You are the {roleinfo.role}
{roleinfo.desc}
-
-
- )}
-
+
+
+
+
+
+
+
+ )}
{!!actions && actions.map(action => (
@@ -49,116 +147,323 @@ export const MafiaPanel = (props, context) => {
))}
- {!!admin_controls && (
+ {!!roleinfo && (
- THESE ARE DEBUG, THEY WILL BREAK THE GAME, DO NOT TOUCH
- Also because an admin did it: do not gib/delete/etc
- anyone! It will runtime the game to death!
-
-
-
-
-
-
- )}
-
-
- {!!players && players.map(player => (
-
- {!player.alive && (DEAD)}
- {player.votes !== undefined && !!player.alive
- && (Votes : {player.votes} )}
- {
- !!player.actions && player.actions.map(action => {
- return (
- ); })
- }
- )
- )}
-
-
- {!!judgement_phase && (
-
+ title="Judgement"
+ buttons={
+
+ }>
- Use these buttons to vote the accused innocent or guilty!
+ disabled={!judgement_phase}
+ onClick={() => act("vote_innocent")} />
+ {!judgement_phase && (
+
+ There is nobody on trial at the moment.
+
+ )}
+ {!!judgement_phase && (
+
+ It is now time to vote, vote the accused innocent or guilty!
+
+ )}
+ disabled={!judgement_phase}
+ onClick={() => act("vote_guilty")} />
+
+
+
)}
-
-
-
- {!!all_roles && all_roles.map(r => (
-
-
- {r}
-
+
+
+
+
+
+
+
+ }>
+
+ {!!all_roles && all_roles.map(r => (
+
+
+
+ {r}
+
+
+ act("mf_lookup", {
+ atype: r.slice(0, -3),
+ })}
+ />
+
+
+
+ ))}
-
- ))}
-
-
-
-
- {roleinfo !== undefined && !!roleinfo.action_log
- && roleinfo.action_log.map(log_line => (
-
- {log_line}
-
- ))}
-
+
+ {!!roleinfo && (
+
+
+ {roleinfo !== undefined && !!roleinfo.action_log
+ && roleinfo.action_log.map(log_line => (
+
+ {log_line}
+
+ ))}
+
+
+ )}
+
+
+
+ )}
+
+
+ {!!admin_controls && (
+
+
+ act("next_phase")} />
+ day voting, day voting > night/trial)
+ pretty fun to just spam this and freak people out,
+ try that roundend!`}
+ content="Next Phase"
+ onClick={() => act("next_phase")} />
+ act("players_home")} />
+ act("new_game")} />
+ act("nuke")} />
+
+ act("debug_setup")} />
+ act("cancel_setup")} />
+
+
+ )}
);
};
+
+const LobbyDisplay = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ phase,
+ timeleft,
+ admin_controls,
+ } = data;
+ return (
+
+ [Phase = {phase} | ]{' '}
+ act("mf_signup")} />
+ act("mf_spectate")} />
+ {!!admin_controls && (
+
+ )}
+
+ );
+};
diff --git a/tgui/packages/tgui/styles/themes/neutral.scss b/tgui/packages/tgui/styles/themes/neutral.scss
new file mode 100644
index 0000000000..05eab7537c
--- /dev/null
+++ b/tgui/packages/tgui/styles/themes/neutral.scss
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2020 Aleksej Komarov
+ * SPDX-License-Identifier: MIT
+ */
+
+ @use 'sass:color';
+ @use 'sass:meta';
+
+ $neutral: #ffb300;
+
+ @use '../colors.scss' with (
+ $primary: $neutral,
+ $fg-map-keys: (),
+ $bg-map-keys: (),
+ );
+ @use '../base.scss' with (
+ $color-bg: color.scale($neutral, $lightness: -40%),
+ $color-bg-grad-spread: 6%,
+ );
+
+ .theme-neutral {
+ // Components
+ @include meta.load-css('../components/Button.scss', $with: (
+ 'color-default': color.scale($neutral, $lightness: -30%),
+ 'color-transparent-text': color.scale($neutral, $lightness: 30%),
+ ));
+ @include meta.load-css('../components/ProgressBar.scss', $with: (
+ 'color-default': $neutral,
+ 'color-background': rgba(0, 0, 0, 0.5),
+ ));
+ @include meta.load-css('../components/Section.scss');
+
+ // Layouts
+ @include meta.load-css('../layouts/Layout.scss');
+ @include meta.load-css('../layouts/Window.scss');
+ @include meta.load-css('../layouts/TitleBar.scss', $with: (
+ 'color-background': color.scale($neutral, $lightness: -25%),
+ ));
+
+ .Layout__content {
+ background-image: url('../../assets/bg-neutral.svg');
+ }
+ }