mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-16 17:43:35 +01:00
[TGUI] Changeling evolution menu (#14711)
* evo menu tgui * mochi review tweaks and tgui travis nitpick * yeah how about I don't fuck it up this time? kthx * rebase for DB 14 and fixing bundle conflict * Fix TGUI Bundle Conflicts Co-authored-by: variableundefined <40092670+variableundefined@users.noreply.github.com>
This commit is contained in:
@@ -1,300 +1,88 @@
|
||||
GLOBAL_LIST_EMPTY(sting_paths)
|
||||
// totally stolen from the new player panel. YAYY
|
||||
/// The evolution menu will be shown in the compact mode, with only powers and costs being displayed.
|
||||
#define COMPACT_MODE 0
|
||||
/// The evolution menu will be shown in the expanded mode, with powers, costs, and ability descriptions being displayed.
|
||||
#define EXPANDED_MODE 1
|
||||
|
||||
/datum/action/changeling/evolution_menu
|
||||
name = "-Evolution Menu-" //Dashes are so it's listed before all the other abilities.
|
||||
desc = "Choose our method of subjugation."
|
||||
button_icon_state = "changelingsting"
|
||||
dna_cost = 0
|
||||
/// Which UI view will be displayed. Compact mode will show only ability names, and will leave out their descriptions and helptext.
|
||||
var/view_mode = EXPANDED_MODE
|
||||
/// A list containing the names of bought changeling abilities. For use with the UI.
|
||||
var/list/purchased_abilities = list()
|
||||
/// A list containing every purchasable changeling ability. Includes its name, description, helptext and cost.
|
||||
var/static/list/ability_list = list()
|
||||
|
||||
/datum/action/changeling/evolution_menu/Grant(mob/M)
|
||||
. = ..()
|
||||
if(length(ability_list))
|
||||
return // List is already populated.
|
||||
|
||||
for(var/action in subtypesof(/datum/action/changeling))
|
||||
var/datum/action/changeling/C = action
|
||||
if(initial(C.dna_cost) <= 0) // Filter out innate abilities like DNA sting, Evolution menu, etc.
|
||||
continue
|
||||
ability_list += list(list(
|
||||
"name" = initial(C.name),
|
||||
"description" = initial(C.desc),
|
||||
"helptext" = initial(C.helptext),
|
||||
"cost" = initial(C.dna_cost)
|
||||
))
|
||||
|
||||
/datum/action/changeling/evolution_menu/Trigger()
|
||||
if(!usr || !usr.mind || !usr.mind.changeling)
|
||||
return
|
||||
var/datum/changeling/changeling = usr.mind.changeling
|
||||
tgui_interact(owner)
|
||||
|
||||
if(!GLOB.sting_paths || !GLOB.sting_paths.len)
|
||||
GLOB.sting_paths = init_subtypes(/datum/action/changeling)
|
||||
/datum/action/changeling/evolution_menu/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_always_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "EvolutionMenu", "Evolution Menu", 480, 574, master_ui, state)
|
||||
ui.set_autoupdate(FALSE)
|
||||
ui.open()
|
||||
|
||||
var/dat = create_menu(changeling)
|
||||
usr << browse(dat, "window=powers;size=600x700")//900x480
|
||||
/datum/action/changeling/evolution_menu/tgui_data(mob/user)
|
||||
var/datum/changeling/cling = owner.mind.changeling
|
||||
var/list/data = list(
|
||||
"can_respec" = cling.canrespec,
|
||||
"evo_points" = cling.geneticpoints,
|
||||
"purchsed_abilities" = purchased_abilities,
|
||||
"view_mode" = view_mode
|
||||
)
|
||||
return data
|
||||
|
||||
/datum/action/changeling/evolution_menu/proc/create_menu(var/datum/changeling/changeling)
|
||||
var/dat
|
||||
dat +="<html><head><title>Changeling Evolution Menu</title></head>"
|
||||
/datum/action/changeling/evolution_menu/tgui_static_data(mob/user)
|
||||
var/list/data = list(
|
||||
"ability_list" = ability_list
|
||||
)
|
||||
return data
|
||||
|
||||
//javascript, the part that does most of the work~
|
||||
dat += {"
|
||||
|
||||
<head>
|
||||
<script type='text/javascript'>
|
||||
|
||||
var locked_tabs = new Array();
|
||||
|
||||
function updateSearch(){
|
||||
|
||||
|
||||
var filter_text = document.getElementById('filter');
|
||||
var filter = filter_text.value.toLowerCase();
|
||||
|
||||
if(complete_list != null && complete_list != ""){
|
||||
var mtbl = document.getElementById("maintable_data_archive");
|
||||
mtbl.innerHTML = complete_list;
|
||||
}
|
||||
|
||||
if(filter.value == ""){
|
||||
return;
|
||||
}else{
|
||||
|
||||
var maintable_data = document.getElementById('maintable_data');
|
||||
var ltr = maintable_data.getElementsByTagName("tr");
|
||||
for( var i = 0; i < ltr.length; ++i )
|
||||
{
|
||||
try{
|
||||
var tr = ltr\[i\];
|
||||
if(tr.getAttribute("id").indexOf("data") != 0){
|
||||
continue;
|
||||
}
|
||||
var ltd = tr.getElementsByTagName("td");
|
||||
var td = ltd\[0\];
|
||||
var lsearch = td.getElementsByTagName("b");
|
||||
var search = lsearch\[0\];
|
||||
//var inner_span = li.getElementsByTagName("span")\[1\] //Should only ever contain one element.
|
||||
//document.write("<p>"+search.innerText+"<br>"+filter+"<br>"+search.innerText.indexOf(filter))
|
||||
if( search.innerText.toLowerCase().indexOf(filter) == -1 )
|
||||
{
|
||||
//document.write("a");
|
||||
//ltr.removeChild(tr);
|
||||
td.innerHTML = "";
|
||||
i--;
|
||||
}
|
||||
}catch(err) { }
|
||||
}
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
var index = -1;
|
||||
var debug = document.getElementById("debug");
|
||||
|
||||
locked_tabs = new Array();
|
||||
|
||||
}
|
||||
|
||||
function expand(id,name,desc,helptext,power,ownsthis){
|
||||
|
||||
clearAll();
|
||||
|
||||
var span = document.getElementById(id);
|
||||
|
||||
body = "<table><tr><td>";
|
||||
|
||||
body += "</td><td align='center'>";
|
||||
|
||||
body += "<font size='2'><b>"+desc+"</b></font> <BR>"
|
||||
|
||||
body += "<font size='2'><span class='danger'>"+helptext+"</span></font> <BR>"
|
||||
|
||||
if(!ownsthis)
|
||||
{
|
||||
body += "<a href='?src=[UID()];P="+power+"'>Evolve</a>"
|
||||
}
|
||||
body += "</td><td align='center'>";
|
||||
|
||||
body += "</td></tr></table>";
|
||||
|
||||
|
||||
span.innerHTML = body
|
||||
}
|
||||
|
||||
function clearAll(){
|
||||
var spans = document.getElementsByTagName('span');
|
||||
for(var i = 0; i < spans.length; i++){
|
||||
var span = spans\[i\];
|
||||
|
||||
var id = span.getAttribute("id");
|
||||
|
||||
if(!(id.indexOf("item")==0))
|
||||
continue;
|
||||
|
||||
var pass = 1;
|
||||
|
||||
for(var j = 0; j < locked_tabs.length; j++){
|
||||
if(locked_tabs\[j\]==id){
|
||||
pass = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(pass != 1)
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
|
||||
span.innerHTML = "";
|
||||
}
|
||||
}
|
||||
|
||||
function addToLocked(id,link_id,notice_span_id){
|
||||
var link = document.getElementById(link_id);
|
||||
var decision = link.getAttribute("name");
|
||||
if(decision == "1"){
|
||||
link.setAttribute("name","2");
|
||||
}else{
|
||||
link.setAttribute("name","1");
|
||||
removeFromLocked(id,link_id,notice_span_id);
|
||||
return;
|
||||
}
|
||||
|
||||
var pass = 1;
|
||||
for(var j = 0; j < locked_tabs.length; j++){
|
||||
if(locked_tabs\[j\]==id){
|
||||
pass = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!pass)
|
||||
return;
|
||||
locked_tabs.push(id);
|
||||
var notice_span = document.getElementById(notice_span_id);
|
||||
notice_span.innerHTML = "<span class='danger'>Locked</span> ";
|
||||
//link.setAttribute("onClick","attempt('"+id+"','"+link_id+"','"+notice_span_id+"');");
|
||||
//document.write("removeFromLocked('"+id+"','"+link_id+"','"+notice_span_id+"')");
|
||||
//document.write("aa - "+link.getAttribute("onClick"));
|
||||
}
|
||||
|
||||
function attempt(ab){
|
||||
return ab;
|
||||
}
|
||||
|
||||
function removeFromLocked(id,link_id,notice_span_id){
|
||||
//document.write("a");
|
||||
var index = 0;
|
||||
var pass = 0;
|
||||
for(var j = 0; j < locked_tabs.length; j++){
|
||||
if(locked_tabs\[j\]==id){
|
||||
pass = 1;
|
||||
index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!pass)
|
||||
return;
|
||||
locked_tabs\[index\] = "";
|
||||
var notice_span = document.getElementById(notice_span_id);
|
||||
notice_span.innerHTML = "";
|
||||
//var link = document.getElementById(link_id);
|
||||
//link.setAttribute("onClick","addToLocked('"+id+"','"+link_id+"','"+notice_span_id+"')");
|
||||
}
|
||||
|
||||
function selectTextField(){
|
||||
var filter_text = document.getElementById('filter');
|
||||
filter_text.focus();
|
||||
filter_text.select();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
||||
"}
|
||||
|
||||
//body tag start + onload and onkeypress (onkeyup) javascript event calls
|
||||
dat += "<body onload='selectTextField(); updateSearch();' onkeyup='updateSearch();'>"
|
||||
|
||||
//title + search bar
|
||||
dat += {"
|
||||
|
||||
<table width='560' align='center' cellspacing='0' cellpadding='5' id='maintable'>
|
||||
<tr id='title_tr'>
|
||||
<td align='center'>
|
||||
<font size='5'><b>Changeling Evolution Menu</b></font><br>
|
||||
Hover over a power to see more information<br>
|
||||
Current ability choices remaining: [changeling.geneticpoints]<br>
|
||||
By rendering a lifeform to a husk, we gain enough power to alter and adapt our evolutions.<br>
|
||||
(<a href='?src=[UID()];readapt=1'>Readapt</a>)<br>
|
||||
<p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id='search_tr'>
|
||||
<td align='center'>
|
||||
<b>Search:</b> <input type='text' id='filter' value='' style='width:300px;'>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
"}
|
||||
|
||||
//player table header
|
||||
dat += {"
|
||||
<span id='maintable_data_archive'>
|
||||
<table width='560' align='center' cellspacing='0' cellpadding='5' id='maintable_data'>"}
|
||||
|
||||
var/i = 1
|
||||
for(var/datum/action/changeling/cling_power in GLOB.sting_paths)
|
||||
|
||||
if(cling_power.dna_cost <= 0) //Let's skip the crap we start with. Keeps the evolution menu uncluttered.
|
||||
continue
|
||||
|
||||
var/ownsthis = changeling.has_sting(cling_power)
|
||||
|
||||
var/color
|
||||
if(ownsthis)
|
||||
if(i%2 == 0)
|
||||
color = "#d8ebd8"
|
||||
else
|
||||
color = "#c3dec3"
|
||||
else
|
||||
if(i%2 == 0)
|
||||
color = "#f2f2f2"
|
||||
else
|
||||
color = "#e6e6e6"
|
||||
|
||||
|
||||
dat += {"
|
||||
|
||||
<tr id='data[i]' name='[i]' onClick="addToLocked('item[i]','data[i]','notice_span[i]')">
|
||||
<td align='center' bgcolor='[color]'>
|
||||
<span id='notice_span[i]'></span>
|
||||
<a id='link[i]'
|
||||
onmouseover='expand("item[i]","[cling_power.name]","[cling_power.desc]","[cling_power.helptext]","[cling_power]",[ownsthis])'
|
||||
>
|
||||
<b id='search[i]'>Evolve [cling_power][ownsthis ? " - Purchased" : (cling_power.req_dna>changeling.absorbedcount ? " - Requires [cling_power.req_dna] absorptions" : " - Cost: [cling_power.dna_cost]")]</b>
|
||||
</a>
|
||||
<br><span id='item[i]'></span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
"}
|
||||
|
||||
i++
|
||||
|
||||
|
||||
//player table ending
|
||||
dat += {"
|
||||
</table>
|
||||
</span>
|
||||
|
||||
<script type='text/javascript'>
|
||||
var maintable = document.getElementById("maintable_data_archive");
|
||||
var complete_list = maintable.innerHTML;
|
||||
</script>
|
||||
</body></html>
|
||||
"}
|
||||
return dat
|
||||
|
||||
|
||||
/datum/action/changeling/evolution_menu/Topic(href, href_list)
|
||||
..()
|
||||
if(!(iscarbon(usr) && usr.mind && usr.mind.changeling))
|
||||
/datum/action/changeling/evolution_menu/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
if(href_list["P"])
|
||||
usr.mind.changeling.purchasePower(usr, href_list["P"])
|
||||
else if(href_list["readapt"])
|
||||
usr.mind.changeling.lingRespec(usr)
|
||||
var/dat = create_menu(usr.mind.changeling)
|
||||
usr << browse(dat, "window=powers;size=600x700")
|
||||
/////
|
||||
var/datum/changeling/cling = owner.mind.changeling
|
||||
|
||||
switch(action)
|
||||
if("readapt")
|
||||
if(!cling.lingRespec(owner))
|
||||
return FALSE
|
||||
purchased_abilities.Cut()
|
||||
return TRUE
|
||||
|
||||
if("purchase")
|
||||
var/power_name = params["power_name"]
|
||||
if(!cling.purchasePower(owner, power_name))
|
||||
return FALSE
|
||||
purchased_abilities |= power_name
|
||||
return TRUE
|
||||
|
||||
if("set_view_mode")
|
||||
var/new_view_mode = text2num(params["mode"])
|
||||
if(!(new_view_mode in list(COMPACT_MODE, EXPANDED_MODE)))
|
||||
return FALSE
|
||||
view_mode = new_view_mode
|
||||
return TRUE
|
||||
|
||||
/datum/changeling/proc/purchasePower(var/mob/living/carbon/user, var/sting_name)
|
||||
var/datum/action/changeling/thepower = null
|
||||
@@ -306,46 +94,47 @@ GLOBAL_LIST_EMPTY(sting_paths)
|
||||
|
||||
if(thepower == null)
|
||||
to_chat(user, "This is awkward. Changeling power purchase failed, please report this bug to a coder!")
|
||||
return
|
||||
return FALSE
|
||||
|
||||
if(absorbedcount < thepower.req_dna)
|
||||
to_chat(user, "We lack the energy to evolve this ability!")
|
||||
return
|
||||
return FALSE
|
||||
|
||||
if(has_sting(thepower))
|
||||
to_chat(user, "We have already evolved this ability!")
|
||||
return
|
||||
return FALSE
|
||||
|
||||
if(thepower.dna_cost < 0)
|
||||
to_chat(user, "We cannot evolve this ability.")
|
||||
return
|
||||
return FALSE
|
||||
|
||||
if(geneticpoints < thepower.dna_cost)
|
||||
to_chat(user, "We have reached our capacity for abilities.")
|
||||
return
|
||||
return FALSE
|
||||
|
||||
if(user.status_flags & FAKEDEATH)//To avoid potential exploits by buying new powers while in stasis, which clears your verblist.
|
||||
to_chat(user, "We lack the energy to evolve new abilities right now.")
|
||||
return
|
||||
return FALSE
|
||||
|
||||
geneticpoints -= thepower.dna_cost
|
||||
purchasedpowers += thepower
|
||||
thepower.on_purchase(user)
|
||||
return TRUE
|
||||
|
||||
//Reselect powers
|
||||
/datum/changeling/proc/lingRespec(var/mob/user)
|
||||
if(!ishuman(user) || issmall(user))
|
||||
to_chat(user, "<span class='danger'>We can't remove our evolutions in this form!</span>")
|
||||
return
|
||||
return FALSE
|
||||
if(canrespec)
|
||||
to_chat(user, "<span class='notice'>We have removed our evolutions from this form, and are now ready to readapt.</span>")
|
||||
user.remove_changeling_powers(1)
|
||||
canrespec = 0
|
||||
user.make_changeling(FALSE)
|
||||
return 1
|
||||
return TRUE
|
||||
else
|
||||
to_chat(user, "<span class='danger'>You lack the power to readapt your evolutions!</span>")
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
/mob/proc/make_changeling(var/get_free_powers = TRUE)
|
||||
if(!mind)
|
||||
|
||||
@@ -93,6 +93,8 @@
|
||||
|
||||
changeling.isabsorbing = 0
|
||||
changeling.canrespec = 1
|
||||
var/datum/action/changeling/evolution_menu/E = locate() in user.actions
|
||||
SStgui.update_uis(E)
|
||||
|
||||
target.death(0)
|
||||
target.Drain()
|
||||
|
||||
@@ -21,6 +21,7 @@ import { createStore, StoreProvider } from './store';
|
||||
import './styles/main.scss';
|
||||
import './styles/themes/cardtable.scss';
|
||||
import './styles/themes/hackerman.scss';
|
||||
import './styles/themes/changeling.scss';
|
||||
import './styles/themes/malfunction.scss';
|
||||
import './styles/themes/ntos.scss';
|
||||
import './styles/themes/retro.scss';
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
import { Fragment } from "inferno";
|
||||
import { useBackend } from "../backend";
|
||||
import { Box, Button, Flex, Section } from "../components";
|
||||
import { Window } from "../layouts";
|
||||
|
||||
export const EvolutionMenu = (props, context) => {
|
||||
return (
|
||||
<Window resizable theme="changeling">
|
||||
<Window.Content className="Layout__content--flexColumn">
|
||||
<EvolutionPoints />
|
||||
<Abilities />
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const EvolutionPoints = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
evo_points,
|
||||
can_respec,
|
||||
} = data;
|
||||
return (
|
||||
<Section title="Evolution Points" height={5.5}>
|
||||
<Flex>
|
||||
<Flex.Item mt={0.5} color="label">
|
||||
Points remaining:
|
||||
</Flex.Item>
|
||||
<Flex.Item mt={0.5} ml={2} bold color="#1b945c">
|
||||
{evo_points}
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
<Button
|
||||
ml={2.5}
|
||||
disabled={!can_respec}
|
||||
content="Readapt"
|
||||
icon="sync"
|
||||
onClick={() => act('readapt')}
|
||||
/>
|
||||
<Button
|
||||
tooltip="By transforming a humanoid into a husk, \
|
||||
we gain the ability to readapt our chosen evolutions."
|
||||
tooltipPosition="bottom"
|
||||
icon="question-circle"
|
||||
/>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const Abilities = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
evo_points,
|
||||
ability_list,
|
||||
purchsed_abilities,
|
||||
view_mode,
|
||||
} = data;
|
||||
return (
|
||||
<Section
|
||||
title="Abilities"
|
||||
flexGrow="1"
|
||||
buttons={
|
||||
<Fragment>
|
||||
<Button
|
||||
icon={!view_mode ? "check-square-o" : "square-o"}
|
||||
selected={!view_mode}
|
||||
content="Compact"
|
||||
onClick={() => act('set_view_mode', {
|
||||
mode: 0,
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
icon={view_mode ? "check-square-o" : "square-o"}
|
||||
selected={view_mode}
|
||||
content="Expanded"
|
||||
onClick={() => act('set_view_mode', {
|
||||
mode: 1,
|
||||
})}
|
||||
/>
|
||||
</Fragment>
|
||||
}>
|
||||
{ability_list.map((ability, i) => (
|
||||
<Box key={i} p={0.5} mx={-1} className="candystripe">
|
||||
<Flex align="center">
|
||||
<Flex.Item ml={0.5} color="#dedede">
|
||||
{ability.name}
|
||||
</Flex.Item>
|
||||
{purchsed_abilities.includes(ability.name) && (
|
||||
<Flex.Item ml={2} bold color="#1b945c">
|
||||
(Purchased)
|
||||
</Flex.Item>
|
||||
)}
|
||||
<Flex.Item mr={3} textAlign="right" grow={1}>
|
||||
<Box as="span" color="label">
|
||||
Cost: {' '}
|
||||
</Box>
|
||||
<Box as="span" bold color="#1b945c">
|
||||
{ability.cost}
|
||||
</Box>
|
||||
</Flex.Item>
|
||||
<Flex.Item textAlign="right">
|
||||
<Button
|
||||
mr={0.5}
|
||||
disabled={(ability.cost > evo_points) || purchsed_abilities.includes(ability.name)}
|
||||
content="Evolve"
|
||||
onClick={() => act('purchase', {
|
||||
power_name: ability.name,
|
||||
})}
|
||||
/>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
{!!view_mode && (
|
||||
<Flex color="#8a8a8a" my={1} ml={1.5} width="95%">
|
||||
{ability.description + ' ' + ability.helptext}
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,36 @@
|
||||
@use 'sass:color';
|
||||
@use 'sass:meta';
|
||||
|
||||
@use '../colors.scss' with (
|
||||
$primary: #563d6b,
|
||||
$fg-map-keys: (),
|
||||
$bg-map-keys: (),
|
||||
);
|
||||
@use '../base.scss' with (
|
||||
$color-bg: #2e2633,
|
||||
$color-bg-grad-spread: 6%,
|
||||
$border-radius: 2px,
|
||||
);
|
||||
|
||||
.theme-changeling {
|
||||
// Atomic classes
|
||||
@include meta.load-css('../atomic/color.scss');
|
||||
|
||||
// Components
|
||||
@include meta.load-css('../components/Button.scss', $with: (
|
||||
'color-default': colors.$primary,
|
||||
'color-selected': #188552,
|
||||
));
|
||||
@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': #352d3b,
|
||||
));
|
||||
|
||||
.Layout__content {
|
||||
background-image: none;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user