hewwo have you tried to work?

This commit is contained in:
Letter N
2020-08-10 10:50:07 +08:00
parent 2f1b769ae4
commit a4040ace26
10 changed files with 634 additions and 146 deletions

View File

@@ -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')
..()

View File

@@ -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())

View File

@@ -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("<b>[loser.body.real_name] wins the day vote, Listen to their defense and vote \"INNOCENT\" or \"GUILTY\"!</b>")
//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("<span class='green'>[role.body.real_name] voted innocent.</span>")
for(var/ii in judgement_guilty_votes)
for(var/ii in judgement_abstain_votes)
var/datum/mafia_role/role = ii
send_message("<span class='comradio'>[role.body.real_name] abstained.</span>")
for(var/iii in judgement_guilty_votes)
var/datum/mafia_role/role = iii
send_message("<span class='red'>[role.body.real_name] voted guilty.</span>")
if(judgement_guilty_votes.len > judgement_innocent_votes.len) //strictly need majority guilty to lynch
send_message("<span class='red'><b>Guilty wins majority, [on_trial.body.real_name] has been lynched.</b></span>")
@@ -225,9 +242,6 @@
else
send_message("<span class='green'><b>Innocent wins majority, [on_trial.body.real_name] has been spared.</b></span>")
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, "<span class='warning'>Wait for the round to start.</span>")
return
if(GLOB.mafia_signup[C.ckey])
GLOB.mafia_signup -= C.ckey
to_chat(usr, "<span class='notice'>You unregister from Mafia.</span>")
return
else
GLOB.mafia_signup[C.ckey] = C
to_chat(usr, "<span class='notice'>You sign up for Mafia.</span>")
if(phase == MAFIA_PHASE_SETUP)
check_signups()
try_autostart()
if("mf_spectate")
if(C.ckey in spectators)
to_chat(usr, "<span class='notice'>You will no longer get messages from the game.</span>")
spectators -= C.ckey
else
to_chat(usr, "<span class='notice'>You will now get messages from the game.</span>")
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."

View File

@@ -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("<span class='big clown'>[body.real_name] WAS A CLOWN! HONK! They take down [victim.body.real_name] with their last prank.</span>")
game.send_message("<span class='big clown'>!! CLOWN VICTORY !!</span>")
victim.kill(game,FALSE)

View File

@@ -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, "<span class='warning'>You must be a ghost to join mafia!</span>")
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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 668 B

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,3 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="user-secret" class="svg-inline--fa fa-user-secret fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" opacity=".33">
<path fill="currentColor" d="M383.9 308.3l23.9-62.6c4-10.5-3.7-21.7-15-21.7h-58.5c11-18.9 17.8-40.6 17.8-64v-.3c39.2-7.8 64-19.1 64-31.7 0-13.3-27.3-25.1-70.1-33-9.2-32.8-27-65.8-40.6-82.8-9.5-11.9-25.9-15.6-39.5-8.8l-27.6 13.8c-9 4.5-19.6 4.5-28.6 0L182.1 3.4c-13.6-6.8-30-3.1-39.5 8.8-13.5 17-31.4 50-40.6 82.8-42.7 7.9-70 19.7-70 33 0 12.6 24.8 23.9 64 31.7v.3c0 23.4 6.8 45.1 17.8 64H56.3c-11.5 0-19.2 11.7-14.7 22.3l25.8 60.2C27.3 329.8 0 372.7 0 422.4v44.8C0 491.9 20.1 512 44.8 512h358.4c24.7 0 44.8-20.1 44.8-44.8v-44.8c0-48.4-25.8-90.4-64.1-114.1zM176 480l-41.6-192 49.6 32 24 40-32 120zm96 0l-32-120 24-40 49.6-32L272 480zm41.7-298.5c-3.9 11.9-7 24.6-16.5 33.4-10.1 9.3-48 22.4-64-25-2.8-8.4-15.4-8.4-18.3 0-17 50.2-56 32.4-64 25-9.5-8.8-12.7-21.5-16.5-33.4-.8-2.5-6.3-5.7-6.3-5.8v-10.8c28.3 3.6 61 5.8 96 5.8s67.7-2.1 96-5.8v10.8c-.1.1-5.6 3.2-6.4 5.8z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -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';

View File

@@ -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 (
<Window
title="Mafia"
theme={role_theme}
width={650}
height={550}
resizable>
<Window.Content>
<Section title={phase}>
{!!roleinfo && (
<Fragment>
<Box>
<TimeDisplay auto="down" value={timeleft} />
width={650} // 414 or 415 / 444 or 445
height={293 + playerAddedHeight}>
<Window.Content scrollable={admin_controls}>
{!roleinfo && (
<Flex scrollable
overflowY="scroll"
direction="column"
height="100%"
grow={1}>
<Section
title="Lobby"
mb={1}
buttons={
<LobbyDisplay
phase={phase}
timeleft={timeleft}
admin_controls={admin_controls} />
}>
<Box textAlign="center">
<NoticeBox info>
The lobby currently has {readyGhosts.length}
/12 valid players signed up.
</NoticeBox>
<Flex
direction="column">
{!!lobbydata && lobbydata.map(lobbyist => (
<Flex.Item
key={lobbyist}
basis={2}
className="Section__title candystripe">
<Flex
height={2}
align="center"
justify="space-between">
<Flex.Item basis={0}>
{lobbyist.name}
</Flex.Item>
<Flex.Item>
STATUS:
</Flex.Item>
<Flex.Item width="30%">
<Section>
<Box
color={
lobbyist.status === "Ready" ? "green" : "red"
}
textAlign="center">
{lobbyist.status} {lobbyist.spectating}
</Box>
</Section>
</Flex.Item>
</Flex>
</Flex.Item>
))}
</Flex>
</Box>
</Section>
</Flex>
)}
{!!roleinfo && (
<Section
title={phase}
minHeight="100px"
maxHeight="50px"
buttons={
<Box>
<b>You are a {roleinfo.role}</b>
{!!admin_controls && (
<Button
color="red"
icon="gavel"
tooltipPosition="bottom-left"
tooltip={multiline`
Hello admin! If it is the admin controls you seek,
please notice the extra scrollbar you have that players
do not!`}
/>
)} <TimeDisplay auto="down" value={timeleft} />
</Box>
<Box>
}>
<Flex
justify="space-between">
<Flex.Item
align="center"
textAlign="center"
maxWidth="500px">
<b>You are the {roleinfo.role}</b><br />
<b>{roleinfo.desc}</b>
</Box>
</Fragment>
)}
</Section>
</Flex.Item>
<Flex.Item>
<Box
className={classes([
'mafia32x32',
roleinfo.revealed_icon,
])}
style={{
'transform': 'scale(2) translate(0px, 10%)',
'vertical-align': 'middle',
}} />
<Box
className={classes([
'mafia32x32',
roleinfo.hud_icon,
])}
style={{
'transform': 'scale(2) translate(-5px, -5px)',
'vertical-align': 'middle',
}} />
</Flex.Item>
</Flex>
</Section>
)}
<Flex>
{!!actions && actions.map(action => (
<Flex.Item key={action}>
@@ -49,116 +147,323 @@ export const MafiaPanel = (props, context) => {
</Flex.Item>
))}
</Flex>
{!!admin_controls && (
{!!roleinfo && (
<Section
title="ADMIN CONTROLS"
backgroundColor="red">
THESE ARE DEBUG, THEY WILL BREAK THE GAME, DO NOT TOUCH <br />
Also because an admin did it: do not gib/delete/etc
anyone! It will runtime the game to death! <br />
<Button
icon="arrow-right"
onClick={() => act("next_phase")}>
Next Phase
</Button>
<Button
icon="home"
onClick={() => act("players_home")}>
Send All Players Home
</Button>
<Button
icon="radiation"
onClick={() => act("new_game")}>
New Game
</Button>
<br />
<Button
icon="skull"
onClick={() => act("nuke")}
color="black">
Nuke (delete datum + landmarks, hope it fixes everything!)
</Button>
</Section>
)}
<Section title="Players">
<LabeledList>
{!!players && players.map(player => (
<LabeledList.Item
className="candystripe"
key={player.ref}
label={player.name}>
{!player.alive && (<Box color="red">DEAD</Box>)}
{player.votes !== undefined && !!player.alive
&& (<Fragment>Votes : {player.votes} </Fragment>)}
{
!!player.actions && player.actions.map(action => {
return (
<Button
key={action}
onClick={() => act('mf_targ_action', {
atype: action,
target: player.ref,
})}>
{action}
</Button>); })
}
</LabeledList.Item>)
)}
</LabeledList>
</Section>
{!!judgement_phase && (
<Section title="JUDGEMENT">
title="Judgement"
buttons={
<Button
color="transparent"
icon="info"
tooltipPosition="left"
tooltip={multiline`
When someone is on trial, you are in charge of their fate.
Innocent winning means the person on trial can live to see
another day... and in losing they do not. You can go back
to abstaining with the middle button if you reconsider.`}
/>
}>
<Flex justify="space-around">
<Button
icon="smile-beam"
content="INNOCENT!"
color="good"
onClick={() => act("vote_innocent")}>
INNOCENT!
</Button>
Use these buttons to vote the accused innocent or guilty!
disabled={!judgement_phase}
onClick={() => act("vote_innocent")} />
{!judgement_phase && (
<Box>
There is nobody on trial at the moment.
</Box>
)}
{!!judgement_phase && (
<Box>
It is now time to vote, vote the accused innocent or guilty!
</Box>
)}
<Button
icon="angry"
content="GUILTY!"
color="bad"
onClick={() => act("vote_guilty")}>
GUILTY!
</Button>
disabled={!judgement_phase}
onClick={() => act("vote_guilty")} />
</Flex>
<Flex justify="center">
<Button
icon="meh"
content="Abstain"
color="white"
disabled={!judgement_phase}
onClick={() => act("vote_abstain")} />
</Flex>
</Section>
)}
<Flex mt={1} spacing={1}>
<Flex.Item grow={1} basis={0}>
<Section
title="Roles"
minHeight={10}>
{!!all_roles && all_roles.map(r => (
<Box key={r}>
<Flex justify="space-between">
{r}
<Button
content="?"
onClick={() => act("mf_lookup", {
atype: r.slice(0, -3),
})}
/>
{phase !== "No Game" &&(
<Flex spacing={1}>
<Flex.Item grow={2}>
<Section title="Players"
buttons={
<Button
color="transparent"
icon="info"
tooltip={multiline`
This is the list of all the players in
the game, during the day phase you may vote on them and,
depending on your role, select players
at certain phases to use your ability.`}
/>
}>
<Flex
direction="column">
{!!players && players.map(player => (
<Flex.Item
height="30px"
className="Section__title candystripe"
key={player.ref}>
<Flex
height="18px"
justify="space-between"
align="center">
<Flex.Item basis={16} >
{!!player.alive && (<Box>{player.name}</Box>)}
{!player.alive && (
<Box color="red">{player.name}</Box>)}
</Flex.Item>
<Flex.Item>
{!player.alive && (<Box color="red">DEAD</Box>)}
</Flex.Item>
<Flex.Item>
{player.votes !== undefined && !!player.alive
&& (<Fragment>Votes : {player.votes} </Fragment>)}
</Flex.Item>
<Flex.Item grow={1} />
<Flex.Item>
{
!!player.actions && player.actions.map(action => {
return (
<Button
key={action}
onClick={() => act('mf_targ_action', {
atype: action,
target: player.ref,
})}>
{action}
</Button>); })
}
</Flex.Item>
</Flex>
</Flex.Item>)
)}
</Flex>
</Section>
</Flex.Item>
<Flex.Item grow={2}>
<Flex
direction="column"
height="100%">
<Section
title="Roles and Notes"
buttons={
<Fragment>
<Button
color="transparent"
icon="address-book"
tooltipPosition="bottom-left"
tooltip={multiline`
The top section is the roles in the game. You can
press the question mark to get a quick blurb
about the role itself.`}
/>
<Button
color="transparent"
icon="edit"
tooltipPosition="bottom-left"
tooltip={multiline`
The bottom section are your notes. on some roles this
will just be an empty box, but on others it records the
actions of your abilities (so for example, your
detective work revealing a changeling).`}
/>
</Fragment>
}>
<Flex
direction="column">
{!!all_roles && all_roles.map(r => (
<Flex.Item
key={r}
height="30px"
className="Section__title candystripe">
<Flex
height="18px"
align="center"
justify="space-between">
<Flex.Item>
{r}
</Flex.Item>
<Flex.Item
textAlign="right">
<Button
color="transparent"
icon="question"
onClick={() => act("mf_lookup", {
atype: r.slice(0, -3),
})}
/>
</Flex.Item>
</Flex>
</Flex.Item>
))}
</Flex>
</Box>
))}
</Section>
</Flex.Item>
<Flex.Item grow={2} basis={0}>
<Section
title="Notes"
minHeight={10}>
{roleinfo !== undefined && !!roleinfo.action_log
&& roleinfo.action_log.map(log_line => (
<Box key={log_line}>
{log_line}
</Box>
))}
</Section>
</Section>
{!!roleinfo && (
<Flex.Item height={0} grow={1}>
<Section scrollable
fill
overflowY="scroll">
{roleinfo !== undefined && !!roleinfo.action_log
&& roleinfo.action_log.map(log_line => (
<Box key={log_line}>
{log_line}
</Box>
))}
</Section>
</Flex.Item>
)}
</Flex>
</Flex.Item>
</Flex>
)}
<Flex mt={1} direction="column">
<Flex.Item>
{!!admin_controls && (
<Section textAlign="center">
<Collapsible
title="ADMIN CONTROLS"
color="red">
<Button
icon="exclamation-triangle"
color="black"
tooltipPosition="top"
tooltip={multiline`
Almost all of these are all built to help me debug
the game (ow, debugging a 12 player game!) So they are
rudamentary and prone to breaking at the drop of a hat.
Make sure you know what you're doing when you press one.
Also because an admin did it: do not gib/delete/dust
anyone! It will runtime the game to death!`}
content="A Kind, Coder Warning"
onClick={() => act("next_phase")} /><br />
<Button
icon="arrow-right"
tooltipPosition="top"
tooltip={multiline`
This will advance the game to the next phase
(day talk > 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")} />
<Button
icon="home"
tooltipPosition="top"
tooltip={multiline`
Hopefully you won't use this button
often, it's a safety net just in case
mafia players somehow escape (nullspace
redirects to the error room then station)
Either way, VERY BAD IF THAT HAPPENS as
godmoded assistants will run free. Use
this to recollect them then make a bug report.`}
content="Send All Players Home"
onClick={() => act("players_home")} />
<Button
icon="sync-alt"
tooltipPosition="top"
tooltip={multiline`
This immediately ends the game, and attempts to start
another. Nothing will happen if another
game fails to start!`}
content="New Game"
onClick={() => act("new_game")} />
<Button
icon="skull"
tooltipPosition="top"
tooltip={multiline`
Deletes the datum, clears all landmarks, makes mafia
as it was roundstart: nonexistant. Use this if you
really mess things up. You did mess things up, didn't you.`}
content="Nuke"
onClick={() => act("nuke")} />
<br />
<Button
icon="paint-brush"
tooltipPosition="top"
tooltip={multiline`
This is the custom game creator, it is... simple.
You put in roles and until you press CANCEL or FINISH
it will keep letting you add more roles. Assitants
on the bottom because of pathing stuff. Resets after
the round finishes back to 12 player random setups.`}
content="Create Custom Setup"
onClick={() => act("debug_setup")} />
<Button
icon="paint-roller"
tooltipPosition="top"
tooltip={multiline`
If you messed up and accidently didn't make it how
you wanted, simply just press this to reset it. The game
will auto reset after each game as well.`}
content="Reset Custom Setup"
onClick={() => act("cancel_setup")} />
</Collapsible>
</Section>
)}
</Flex.Item>
</Flex>
</Window.Content>
</Window>
);
};
const LobbyDisplay = (props, context) => {
const { act, data } = useBackend(context);
const {
phase,
timeleft,
admin_controls,
} = data;
return (
<Box>
[Phase = {phase} | <TimeDisplay auto="down" value={timeleft} />]{' '}
<Button
icon="clipboard-check"
tooltipPosition="bottom-left"
tooltip={multiline`
Signs you up for the next game. If there
is an ongoing one, you will be signed up
for the next.`}
content="Sign Up"
onClick={() => act("mf_signup")} />
<Button
icon="eye"
tooltipPosition="bottom-left"
tooltip={multiline`
Spectates games until you turn it off.
Automatically enabled when you die in game,
because I assumed you would want to see the
conclusion. You won't get messages if you
rejoin SS13.`}
content="Spectate"
onClick={() => act("mf_spectate")} />
{!!admin_controls && (
<Button
color="red"
icon="gavel"
tooltipPosition="bottom-left"
tooltip={multiline`
Hello admin! If it is the admin controls you seek,
please notice the scrollbar you have that players
do not!`}
/>
)}
</Box>
);
};

View File

@@ -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');
}
}