1 /** default console commands */
2 
3 module re.ng.diag.default_commands;
4 
5 import std.range;
6 import std.array;
7 import std.algorithm;
8 import std.string;
9 import std.conv;
10 
11 import re.core;
12 import re.ecs;
13 
14 debug static class DefaultCommands {
15     alias log = Core.log;
16     alias scenes = Core.scenes;
17     alias dbg = Core.debugger;
18     alias con = dbg.console;
19 
20     static void c_help(string[] args) {
21         auto sb = appender!string();
22         sb ~= "available commmands:\n";
23         auto command_names = con.commands.keys.sort();
24         foreach (command_name; command_names) {
25             auto command = con.commands[command_name];
26             sb ~= format("%s - %s\n", command.name, command.help);
27         }
28         log.info(sb.data);
29     }
30 
31     static void c_entities(string[] args) {
32         auto sb = appender!string();
33         sb ~= "entity list:\n";
34         foreach (i, scene; scenes) {
35             // print scene header
36             sb ~= format("▶ Scene[%d]: %s ¬\n", i, typeid(scene).name);
37             foreach (entity; scene.ecs.entities) {
38                 // get list of components
39                 auto component_types = entity.get_all_components().map!(x => x.classinfo.name);
40                 // sb ~= format("  ▷ %s: pos(%s) components[%d] {%s}\n", entity.name,
41                 //         entity.position, entity.components.length, component_types);
42                 sb ~= format("  ▷ %s: pos(%s) components[%s]\n", entity.name, entity.position, component_types
43                         .length);
44                 for (int j = 0; j < component_types.length; j++) {
45                     sb ~= format("    ■ [%s] %s\n", j, component_types[j]);
46                 }
47             }
48         }
49         log.info(sb.data);
50     }
51 
52     private static bool pick_entity(string name, out Entity entity) {
53         // find entities in all scenes
54         foreach (scene; scenes) {
55             if (scene.ecs.has_entity(name)) {
56                 entity = scene.get_entity(name);
57                 log.info(format("selected entity '%s' in scene %s",
58                         entity.name, typeid(scene).name));
59                 return true;
60             }
61         }
62 
63         log.err(format("entity '%s' not found", name));
64         return false;
65     }
66 
67     private static bool pick_component(string[] args, out Component comp) {
68         if (args.length < 2) {
69             log.err("usage: <entity> <component>");
70             return false;
71         }
72         Entity entity;
73         if (!pick_entity(args[0], entity))
74             return false;
75         auto comp_sel = args[1];
76         auto all_comps = entity.get_all_components();
77         // check if we can parse component selector as int
78         try {
79             auto comp_ix = comp_sel.to!int;
80 
81             // select component at this index
82             comp = all_comps[comp_ix];
83         } catch (ConvException) {
84             // not an int, try to find component by name
85             auto comp_search = comp_sel.toLower;
86             // find matching component
87             auto matches = all_comps
88                 .find!(x => x.classinfo.name.toLower.indexOf(comp_search) > 0);
89             if (matches.length == 0) {
90                 log.err(format("no matching component for '%s'", comp_search));
91                 return false;
92             }
93             comp = matches.front;
94         }
95 
96         return true;
97     }
98 
99     static void c_dump(string[] args) {
100         Component comp;
101         if (!pick_component(args, comp))
102             return;
103         // dump this component
104         auto sb = appender!(string);
105         auto comp_class = comp.getMetaType;
106         // log.info(format("dumping: %s", comp_class.getName));
107         sb ~= format("dump: %s\n", comp_class.getName);
108         foreach (field; comp_class.getFields) {
109             sb ~= format("  %s = %s\n", field.getName, field.get(comp));
110         }
111         log.info(sb.data);
112     }
113 
114     static void c_inspect(string[] args) {
115         if (args.length == 0) {
116             if (dbg.inspector.open) {
117                 // close inspector when run without args
118                 dbg.inspector.close();
119             } else {
120                 // inspector isn't open, and no arg was given
121                 log.err("usage: inspect <entity>");
122             }
123             return;
124         }
125         Entity entity;
126         if (!pick_entity(args[0], entity))
127             return;
128         if (dbg.inspector.open)
129             dbg.inspector.close(); // close the existing inspector
130 
131         // attach the inspector to this entity
132         dbg.inspector.inspect(entity);
133     }
134 }