[MIRROR] Modifies "MobSpawner" and Adds ability to set-up mob AI through the interface (#6685)

Co-authored-by: Heroman3003 <31296024+Heroman3003@users.noreply.github.com>
Co-authored-by: Raeschen <rycoop29@gmail.com>
This commit is contained in:
CHOMPStation2
2023-07-30 08:02:32 -07:00
committed by GitHub
parent 9b2f604f3f
commit bba71198fd
4 changed files with 378 additions and 216 deletions

View File

@@ -1,139 +1,185 @@
/datum/eventkit/mob_spawner /datum/eventkit/mob_spawner
// The path of the mob to be spawned // The path of the mob to be spawned
var/path var/path
// Defines if the location of the spawned mob should be bound of the users position //The ai type path to be assigned to the mob
var/loc_lock = FALSE var/use_custom_ai = FALSE
var/ai_type = ""
/datum/eventkit/mob_spawner/New() var/faction = ""
. = ..() var/intent = ""
var/new_path = TRUE //Sets default ai vars based on path. Tracked explicitly because tgui_act wouldn't make it work, used in tgui_data thusly
/datum/eventkit/mob_spawner/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui) // Defines if the location of the spawned mob should be bound of the users position
if(!ui) var/loc_lock = FALSE
ui = new(user, src, "MobSpawner", "EventKit - Mob Spawner")
ui.set_autoupdate(FALSE) /datum/eventkit/mob_spawner/New()
ui.open() . = ..()
/datum/eventkit/mob_spawner/Destroy() /datum/eventkit/mob_spawner/tgui_interact(mob/user, datum/tgui/ui)
. = ..() ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
/datum/eventkit/mob_spawner/tgui_state(mob/user) ui = new(user, src, "MobSpawner", "EventKit - Mob Spawner")
return GLOB.tgui_admin_state ui.set_autoupdate(FALSE)
ui.open()
/datum/eventkit/mob_spawner/tgui_static_data(mob/user)
var/list/data = list() /datum/eventkit/mob_spawner/Destroy()
. = ..()
data["initial_x"] = usr.x;
data["initial_y"] = usr.y; /datum/eventkit/mob_spawner/tgui_state(mob/user)
data["initial_z"] = usr.z; return GLOB.tgui_admin_state
return data /datum/eventkit/mob_spawner/tgui_static_data(mob/user)
var/list/data = list()
/datum/eventkit/mob_spawner/tgui_data(mob/user)
var/list/data = list() data["initial_x"] = usr.x;
data["initial_y"] = usr.y;
data["loc_lock"] = loc_lock; data["initial_z"] = usr.z;
if(loc_lock)
data["loc_x"] = usr.x;
data["loc_y"] = usr.y; return data
data["loc_z"] = usr.z;
/datum/eventkit/mob_spawner/tgui_data(mob/user)
data["path"] = path; var/list/data = list()
if(path) data["loc_lock"] = loc_lock
var/mob/M = new path(); if(loc_lock)
if(M) data["loc_x"] = usr.x
data["default_path_name"] = M.name; data["loc_y"] = usr.y
data["default_desc"] = M.desc; data["loc_z"] = usr.z
data["default_flavor_text"] = M.flavor_text;
qdel(M); data["path"] = path;
data["use_custom_ai"] = use_custom_ai
return data
/datum/eventkit/mob_spawner/tgui_act(action, list/params) if(path)
. = ..() var/mob/M = new path()
if(.) if(M)
return data["default_path_name"] = M.name
if(!check_rights_for(usr.client, R_SPAWN)) data["default_desc"] = M.desc
return data["default_flavor_text"] = M.flavor_text
switch(action) if(new_path && istype(M, /mob/living))
if("select_path") var/mob/living/L = M
var/list/choices = typesof(/mob) ai_type = (L.ai_holder_type ? L.ai_holder_type : /datum/ai_holder/simple_mob/inert)
var/newPath = tgui_input_list(usr, "Please select the new path of the mob you want to spawn.", items = choices) faction = (L.faction ? L.faction : "neutral")
intent = (L.a_intent ? L.a_intent : I_HELP)
path = newPath new_path = FALSE
qdel(L)
return TRUE qdel(M)
if("loc_lock") data["ai_type"] = ai_type
loc_lock = !loc_lock data["faction"] = faction
return TRUE data["intent"] = intent
if("start_spawn")
var/confirm = tgui_alert(usr, "Are you sure that you want to start spawning your custom mobs?", "Confirmation", list("Yes", "Cancel"))
return data
if(confirm != "Yes")
return FALSE /datum/eventkit/mob_spawner/tgui_act(action, list/params)
. = ..()
var/amount = params["amount"] if(.)
var/name = params["name"] return
var/x = params["x"] if(!check_rights_for(usr.client, R_SPAWN))
var/y = params["y"] return
var/z = params["z"] switch(action)
if("select_path")
if(!name) var/list/choices = typesof(/mob)
to_chat(usr, "<span class='warning'>Name cannot be empty.</span>") var/newPath = tgui_input_list(usr, "Please select the new path of the mob you want to spawn.", items = choices)
return FALSE
path = newPath
var/turf/T = locate(x, y, z) new_path = TRUE
if(!T) return TRUE
to_chat(usr, "<span class='warning'>Those coordinates are outside the boundaries of the map.</span>") if("toggle_custom_ai")
return FALSE use_custom_ai = !use_custom_ai
return TRUE
for(var/i = 0, i < amount, i++) if("set_faction")
if(ispath(path,/turf)) faction = sanitize(tgui_input_text(usr, "Please input your mobs' faction", "Faction", (faction ? faction : "neutral")))
var/turf/TU = get_turf(locate(x, y, z)) return TRUE
TU.ChangeTurf(path) if("set_intent")
else intent = tgui_input_list(usr, "Please select preferred intent", "Select Intent", list(I_HELP, I_HURT), (intent ? intent : I_HELP))
var/mob/M = new path(usr.loc) return TRUE
if("set_ai_path")
M.name = sanitize(name) ai_type = tgui_input_list(usr, "Select AI path. Not all subtypes are compatible!", "AI type", \
M.desc = sanitize(params["desc"]) typesof(/datum/ai_holder/), (ai_type ? ai_type : /datum/ai_holder/simple_mob/inert))
M.flavor_text = sanitize(params["flavor_text"]) return TRUE
if("loc_lock")
/* loc_lock = !loc_lock
WIP: Radius around selected coords return TRUE
if("start_spawn")
var/list/turf/destTurfs var/confirm = tgui_alert(usr, "Are you sure that you want to start spawning your custom mobs?", "Confirmation", list("Yes", "Cancel"))
for(var/turf/RT in orange(T, params["r"]))
destTurfs += RT if(confirm != "Yes")
return FALSE
var/turf/targetTurf = rand(0,length(destTurfs))
*/ var/amount = params["amount"]
var/name = params["name"]
var/size_mul = params["size_multiplier"] var/x = params["x"]
if(isnum(size_mul)) var/y = params["y"]
M.size_multiplier = size_mul var/z = params["z"]
M.update_icon()
else if(!name)
to_chat(usr, "<span class='warning'>Size Multiplier not applied: ([size_mul]) is not a valid input.</span>") to_chat(usr, "<span class='warning'>Name cannot be empty.</span>")
return FALSE
M.forceMove(T)
var/turf/T = locate(x, y, z)
log_and_message_admins("spawned [path] ([name]) at ([x],[y],[z]) [amount] times.") if(!T)
to_chat(usr, "<span class='warning'>Those coordinates are outside the boundaries of the map.</span>")
return TRUE return FALSE
/datum/eventkit/mob_spawner/tgui_close(mob/user) for(var/i = 0, i < amount, i++)
. = ..() if(ispath(path,/turf))
qdel(src) var/turf/TU = get_turf(locate(x, y, z))
TU.ChangeTurf(path)
/client/proc/eventkit_open_mob_spawner() else
set category = "Fun" //ChompEDIT - "EventKit" --> "Fun", less tab spam var/mob/M = new path(usr.loc)
set name = "Open Mob Spawner"
set desc = "Opens an advanced version of the mob spawner." M.name = sanitize(name)
M.desc = sanitize(params["desc"])
if(!check_rights(R_SPAWN)) M.flavor_text = sanitize(params["flavor_text"])
return if(use_custom_ai)
if(istype(M, /mob/living))
var/datum/eventkit/mob_spawner/spawner = new() var/mob/living/L = M
spawner.tgui_interact(usr) L.ai_holder_type = ai_type
L.faction = faction
L.a_intent = intent
L.initialize_ai_holder()
L.AdjustSleeping(-100)
else
to_chat(usr, span_notice("You can only set AI for subtypes of mob/living!"))
/*
WIP: Radius around selected coords
var/list/turf/destTurfs
for(var/turf/RT in orange(T, params["r"]))
destTurfs += RT
var/turf/targetTurf = rand(0,length(destTurfs))
*/
var/size_mul = params["size_multiplier"]
if(isnum(size_mul))
M.size_multiplier = size_mul
M.update_icon()
else
to_chat(usr, "<span class='warning'>Size Multiplier not applied: ([size_mul]) is not a valid input.</span>")
M.forceMove(T)
log_and_message_admins("spawned [path] ([name]) at ([x],[y],[z]) [amount] times.")
return TRUE
/datum/eventkit/mob_spawner/tgui_close(mob/user)
. = ..()
qdel(src)
/client/proc/eventkit_open_mob_spawner()
set category = "Fun" //ChompEDIT - "EventKit" --> "Fun", less tab spam
set name = "Open Mob Spawner"
set desc = "Opens an advanced version of the mob spawner."
if(!check_rights(R_SPAWN))
return
var/datum/eventkit/mob_spawner/spawner = new()
spawner.tgui_interact(usr)

View File

@@ -1,6 +1,6 @@
import { BooleanLike } from '../../common/react'; import { BooleanLike } from '../../common/react';
import { useBackend, useLocalState } from '../backend'; import { useBackend, useLocalState } from '../backend';
import { Button, Flex, Input, Knob, LabeledList, NumberInput, Section, Tabs, TextArea } from '../components'; import { Button, Divider, Flex, Input, Knob, LabeledList, NumberInput, Section, Tabs, TextArea } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
type Data = { type Data = {
@@ -10,6 +10,11 @@ type Data = {
default_desc: string; default_desc: string;
default_flavor_text: string; default_flavor_text: string;
use_custom_ai: BooleanLike;
ai_type: string;
faction: string;
intent: string;
default_speak_emotes: string[]; default_speak_emotes: string[];
loc_lock: BooleanLike; loc_lock: BooleanLike;
@@ -58,6 +63,14 @@ const GeneralMobSettings = (props, context) => {
'name', 'name',
data.default_path_name data.default_path_name
); );
const [ai_type] = useLocalState(context, 'aiType', data.ai_type);
const [use_custom_ai] = useLocalState(
context,
'toggleCustomAi',
data.use_custom_ai
);
const [faction] = useLocalState(context, 'setMobFaction', data.faction);
const [intent] = useLocalState(context, 'setIntent', data.intent);
const [desc, setDesc] = useLocalState(context, 'desc', data.default_desc); const [desc, setDesc] = useLocalState(context, 'desc', data.default_desc);
const [flavorText, setFlavorText] = useLocalState( const [flavorText, setFlavorText] = useLocalState(
context, context,
@@ -114,43 +127,88 @@ const GeneralMobSettings = (props, context) => {
</LabeledList.Item> </LabeledList.Item>
</LabeledList> </LabeledList>
</Section> </Section>
<Section title="Positional Settings"> <Section title="General Settings">
<LabeledList> <Flex horizontal>
<LabeledList.Item label="Spawn (X/Y/Z) Coords"> <Flex.Item FlexGrow>
<NumberInput <Section title="Positional Settings">
value={data.loc_lock ? data.loc_x : x} <LabeledList>
minValue={0} <LabeledList.Item label="Spawn (X/Y/Z) Coords">
maxValue={256} <NumberInput
onChange={(e, val) => setX(val)} value={data.loc_lock ? data.loc_x : x}
/> minValue={0}
<NumberInput maxValue={256}
value={data.loc_lock ? data.loc_y : y} onChange={(e, val) => setX(val)}
minValue={0} />
maxValue={256} <NumberInput
onChange={(e, val) => setY(val)} value={data.loc_lock ? data.loc_y : y}
/> minValue={0}
<NumberInput maxValue={256}
value={data.loc_lock ? data.loc_z : z} onChange={(e, val) => setY(val)}
minValue={0} />
maxValue={256} <NumberInput
onChange={(e, val) => setZ(val)} value={data.loc_lock ? data.loc_z : z}
/> minValue={0}
<Button.Checkbox maxValue={256}
content="Lock coords to self" onChange={(e, val) => setZ(val)}
checked={data.loc_lock} />
onClick={() => act('loc_lock')} <Button.Checkbox
/> content="Lock coords to self"
</LabeledList.Item> checked={data.loc_lock}
<LabeledList.Item label="Spawn Radius (WIP)"> onClick={() => act('loc_lock')}
<NumberInput />
value={radius} </LabeledList.Item>
disabled <LabeledList.Item label="Spawn Radius (WIP)">
minValue={0} <NumberInput
maxValue={256} value={radius}
onChange={(e, val) => setRadius(val)} disabled
/> minValue={0}
</LabeledList.Item> maxValue={256}
</LabeledList> onChange={(e, val) => setRadius(val)}
/>
</LabeledList.Item>
</LabeledList>
</Section>
</Flex.Item>
<Flex.Item>
<Divider vertical />
</Flex.Item>
<Flex.Item FlexGrow>
<Section
title="AI settings"
buttons={
<Button
selected={use_custom_ai}
fill
content="Use Custom AI"
onClick={() => act('toggle_custom_ai')}
/>
}>
<LabeledList>
<LabeledList.Item>
<Button
fluid
content={ai_type || 'Choose AI Type'}
onClick={(val) => act('set_ai_path')}
/>
</LabeledList.Item>
<LabeledList.Item>
<Button
fluid
content={faction || 'Set Faction'}
onClick={(val) => act('set_faction')}
/>
</LabeledList.Item>
<LabeledList.Item>
<Button
fluid
content={intent || 'Set Intent'}
onClick={(val) => act('set_intent')}
/>
</LabeledList.Item>
</LabeledList>
</Section>
</Flex.Item>
</Flex>
</Section> </Section>
<Section title="Descriptions"> <Section title="Descriptions">
<Flex> <Flex>

View File

@@ -1,6 +1,6 @@
import { BooleanLike } from '../../common/react'; import { BooleanLike } from '../../common/react';
import { useBackend, useLocalState } from '../backend'; import { useBackend, useLocalState } from '../backend';
import { Button, Flex, Input, Knob, LabeledList, NumberInput, Section, Tabs, TextArea } from '../components'; import { Button, Divider, Flex, Input, Knob, LabeledList, NumberInput, Section, Tabs, TextArea } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
type Data = { type Data = {
@@ -10,6 +10,11 @@ type Data = {
default_desc: string; default_desc: string;
default_flavor_text: string; default_flavor_text: string;
use_custom_ai: BooleanLike;
ai_type: string;
faction: string;
intent: string;
default_speak_emotes: string[]; default_speak_emotes: string[];
loc_lock: BooleanLike; loc_lock: BooleanLike;
@@ -58,6 +63,14 @@ const GeneralMobSettings = (props, context) => {
'name', 'name',
data.default_path_name data.default_path_name
); );
const [ai_type] = useLocalState(context, 'aiType', data.ai_type);
const [use_custom_ai] = useLocalState(
context,
'toggleCustomAi',
data.use_custom_ai
);
const [faction] = useLocalState(context, 'setMobFaction', data.faction);
const [intent] = useLocalState(context, 'setIntent', data.intent);
const [desc, setDesc] = useLocalState(context, 'desc', data.default_desc); const [desc, setDesc] = useLocalState(context, 'desc', data.default_desc);
const [flavorText, setFlavorText] = useLocalState( const [flavorText, setFlavorText] = useLocalState(
context, context,
@@ -114,43 +127,88 @@ const GeneralMobSettings = (props, context) => {
</LabeledList.Item> </LabeledList.Item>
</LabeledList> </LabeledList>
</Section> </Section>
<Section title="Positional Settings"> <Section title="General Settings">
<LabeledList> <Flex horizontal>
<LabeledList.Item label="Spawn (X/Y/Z) Coords"> <Flex.Item FlexGrow>
<NumberInput <Section title="Positional Settings">
value={data.loc_lock ? data.loc_x : x} <LabeledList>
minValue={0} <LabeledList.Item label="Spawn (X/Y/Z) Coords">
maxValue={256} <NumberInput
onChange={(e, val) => setX(val)} value={data.loc_lock ? data.loc_x : x}
/> minValue={0}
<NumberInput maxValue={256}
value={data.loc_lock ? data.loc_y : y} onChange={(e, val) => setX(val)}
minValue={0} />
maxValue={256} <NumberInput
onChange={(e, val) => setY(val)} value={data.loc_lock ? data.loc_y : y}
/> minValue={0}
<NumberInput maxValue={256}
value={data.loc_lock ? data.loc_z : z} onChange={(e, val) => setY(val)}
minValue={0} />
maxValue={256} <NumberInput
onChange={(e, val) => setZ(val)} value={data.loc_lock ? data.loc_z : z}
/> minValue={0}
<Button.Checkbox maxValue={256}
content="Lock coords to self" onChange={(e, val) => setZ(val)}
checked={data.loc_lock} />
onClick={() => act('loc_lock')} <Button.Checkbox
/> content="Lock coords to self"
</LabeledList.Item> checked={data.loc_lock}
<LabeledList.Item label="Spawn Radius (WIP)"> onClick={() => act('loc_lock')}
<NumberInput />
value={radius} </LabeledList.Item>
disabled <LabeledList.Item label="Spawn Radius (WIP)">
minValue={0} <NumberInput
maxValue={256} value={radius}
onChange={(e, val) => setRadius(val)} disabled
/> minValue={0}
</LabeledList.Item> maxValue={256}
</LabeledList> onChange={(e, val) => setRadius(val)}
/>
</LabeledList.Item>
</LabeledList>
</Section>
</Flex.Item>
<Flex.Item>
<Divider vertical />
</Flex.Item>
<Flex.Item FlexGrow>
<Section
title="AI settings"
buttons={
<Button
selected={use_custom_ai}
fill
content="Use Custom AI"
onClick={() => act('toggle_custom_ai')}
/>
}>
<LabeledList>
<LabeledList.Item>
<Button
fluid
content={ai_type || 'Choose AI Type'}
onClick={(val) => act('set_ai_path')}
/>
</LabeledList.Item>
<LabeledList.Item>
<Button
fluid
content={faction || 'Set Faction'}
onClick={(val) => act('set_faction')}
/>
</LabeledList.Item>
<LabeledList.Item>
<Button
fluid
content={intent || 'Set Intent'}
onClick={(val) => act('set_intent')}
/>
</LabeledList.Item>
</LabeledList>
</Section>
</Flex.Item>
</Flex>
</Section> </Section>
<Section title="Descriptions"> <Section title="Descriptions">
<Flex> <Flex>

File diff suppressed because one or more lines are too long