Skip to content

Commit 42afcda

Browse files
committed
feat(cli): add game project template
1 parent cd77ac5 commit 42afcda

9 files changed

Lines changed: 350 additions & 21 deletions

File tree

include/vix/cli/commands/new/NewGenerator.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,18 @@ namespace vix::commands::new_cmd::generator
6666
const std::string &projName,
6767
std::string &err);
6868

69+
/// Generates all files for a Vix Game project under projectDir.
70+
bool generate_game_project(
71+
const fs::path &projectDir,
72+
const std::string &projName,
73+
std::string &err);
74+
6975
// ------------------------------------------------------------------
7076
// Post-generation output
7177
// ------------------------------------------------------------------
7278

7379
void print_next_steps_app(const fs::path &projectDir, const std::string &projName);
7480
void print_next_steps_vue(const fs::path &projectDir, const std::string &projName);
7581
void print_next_steps_lib(const fs::path &projectDir, const std::string &projName);
76-
82+
void print_next_steps_game(const fs::path &projectDir, const std::string &projName);
7783
} // namespace vix::commands::new_cmd::generator

include/vix/cli/commands/new/NewOutput.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ namespace vix::commands::new_cmd::output
4343
const std::filesystem::path &projectDir,
4444
const std::string &projName);
4545

46+
/// Full creation summary for a Game project.
47+
void print_creation_game(
48+
const std::filesystem::path &projectDir,
49+
const std::string &projName);
50+
4651
} // namespace vix::commands::new_cmd::output
4752

4853
#endif

include/vix/cli/commands/new/NewTemplates.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,16 @@ WAF_MODE=basic
171171
std::string make_vue_main_js();
172172
std::string make_vue_app_vue();
173173

174+
// ------------------------------------------------------------------
175+
// Game generators
176+
// ------------------------------------------------------------------
177+
178+
std::string make_game_main_cpp(const std::string &projectName);
179+
std::string make_game_package_json(const std::string &projectName);
180+
std::string make_readme_game(const std::string &projectName);
181+
std::string make_project_manifest_game(const std::string &projectName);
182+
std::string make_vix_json_game(const std::string &projectName);
183+
174184
// ------------------------------------------------------------------
175185
// CMakeLists.txt generators
176186
// ------------------------------------------------------------------

include/vix/cli/commands/new/NewTypes.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ namespace vix::commands::new_cmd
1818
{
1919
App,
2020
Lib,
21-
Vue
21+
Vue,
22+
Game
2223
};
2324

2425
struct FeaturesSelection

src/commands/NewCommand.cpp

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ namespace vix::commands::NewCommand
9999
if (argsIn.empty())
100100
{
101101
error("Missing project name.");
102-
hint("Usage: vix new <name|path> [-d|--dir <base_dir>] [--app|--lib] [--force]");
102+
hint("Usage: vix new <name|path> [-d|--dir <base_dir>] [--app|--lib|--game] [--force]");
103103
return 1;
104104
}
105105

@@ -112,47 +112,63 @@ namespace vix::commands::NewCommand
112112

113113
const bool wantsLib = has_any(args, {"--lib", "--library", "--type=lib", "--type=library"});
114114
const bool wantsApp = has_any(args, {"--app", "--application", "--type=app", "--type=application"});
115+
const bool wantsGame = has_any(args, {"--game", "--type=game"});
115116

116117
for (const auto &f : {"--lib", "--library", "--type=lib", "--type=library",
117-
"--app", "--application", "--type=app", "--type=application"})
118+
"--app", "--application", "--type=app", "--type=application",
119+
"--game", "--type=game"})
118120
consume_flag(args, f);
119121

120122
if (args.empty())
121123
{
122124
error("Missing project name.");
123-
hint("Usage: vix new <name|path> [--template vue] [--app|--lib] [--force]");
125+
hint("Usage: vix new <name|path> [--template vue|game] [--app|--lib|--game] [--force]");
124126
return 1;
125127
}
126128

127-
if (!args[0].empty() && args[0][0] == '-' && !gen::is_dot_path(args[0]))
129+
if ((wantsLib && wantsApp) || (wantsLib && wantsGame) || (wantsApp && wantsGame))
128130
{
129-
error("Missing project name.");
130-
hint("Options must come after a project name or be followed by one.");
131-
step("vix new my-app --template vue");
131+
error("Conflicting options: choose only one project type.");
132132
return 1;
133133
}
134134

135-
if (wantsLib && wantsApp)
135+
if (!args[0].empty() && args[0][0] == '-' && !gen::is_dot_path(args[0]))
136136
{
137-
error("Conflicting options: choose either --app or --lib.");
137+
error("Missing project name.");
138+
hint("Options must come after a project name or be followed by one.");
139+
step("vix new my-app --template vue");
138140
return 1;
139141
}
140142

141143
if (templateOpt.has_value())
142144
{
143145
const std::string tpl = *templateOpt;
144146

145-
if (tpl != "vue")
147+
if (tpl != "vue" && tpl != "game")
146148
{
147149
error("Unknown template: " + tpl);
148-
hint("Supported templates: vue");
150+
hint("Supported templates: vue, game");
149151
return 1;
150152
}
151153

152154
if (wantsLib)
153155
{
154-
error("Conflicting options: --template vue cannot be used with --lib.");
155-
hint("Vue templates generate applications.");
156+
error("Conflicting options: --template cannot be used with --lib.");
157+
hint("Templates currently supported: vue, game.");
158+
return 1;
159+
}
160+
161+
if (wantsApp && *templateOpt == "game")
162+
{
163+
error("Conflicting options: --template game cannot be used with --app.");
164+
hint("Use --game or --template game.");
165+
return 1;
166+
}
167+
168+
if (wantsGame && *templateOpt == "vue")
169+
{
170+
error("Conflicting options: --template vue cannot be used with --game.");
171+
hint("Use --app, --template vue, or --template game.");
156172
return 1;
157173
}
158174
}
@@ -277,10 +293,18 @@ namespace vix::commands::NewCommand
277293
// ------------------------------------------------------------------
278294
TemplateKind kind = TemplateKind::App;
279295

280-
if (templateOpt.has_value() && *templateOpt == "vue")
296+
if (templateOpt.has_value() && *templateOpt == "game")
297+
{
298+
kind = TemplateKind::Game;
299+
}
300+
else if (templateOpt.has_value() && *templateOpt == "vue")
281301
{
282302
kind = TemplateKind::Vue;
283303
}
304+
else if (wantsGame)
305+
{
306+
kind = TemplateKind::Game;
307+
}
284308
else if (wantsLib)
285309
{
286310
kind = TemplateKind::Lib;
@@ -353,6 +377,21 @@ namespace vix::commands::NewCommand
353377
return 0;
354378
}
355379

380+
if (kind == TemplateKind::Game)
381+
{
382+
if (!gen::generate_game_project(projectDir, projName, genErr))
383+
{
384+
vix::cli::util::err_line(std::cerr, "Failed to create project files.");
385+
vix::cli::util::warn_line(std::cerr, genErr);
386+
return 1;
387+
}
388+
389+
vix::cli::util::ok_line(std::cout, "Project created.");
390+
vix::cli::util::kv(std::cout, "Location", projectDir.string());
391+
gen::print_next_steps_game(projectDir, projName);
392+
return 0;
393+
}
394+
356395
if (kind == TemplateKind::App)
357396
{
358397
if (!gen::generate_app_project(projectDir, projName, features, genErr))
@@ -401,7 +440,9 @@ namespace vix::commands::NewCommand
401440
<< " vix new api\n"
402441
<< " vix new .\n"
403442
<< " vix new tree --lib\n"
443+
<< " vix new mario --game\n"
404444
<< " vix new shop --template vue\n"
445+
<< " vix new platformer --template game\n"
405446
<< " vix new blog -d ./projects\n"
406447
<< " vix new api --force\n\n"
407448

@@ -411,12 +452,12 @@ namespace vix::commands::NewCommand
411452
<< " • Creates a vix.json manifest\n"
412453
<< " • For apps, creates an executable target matching the project name\n"
413454
<< " • For libraries, creates a header-only CMake interface target\n"
414-
<< " • Applies the selected template (app or library)\n\n"
455+
<< " • Applies the selected template (app, game, library, or Vue)\n\n"
415456

416-
<< "Options\n"
417457
<< " --app Generate an application (default)\n"
458+
<< " --game Generate a Vix game project\n"
418459
<< " --lib Generate a header-only library\n"
419-
<< " --template Project template, currently: vue\n"
460+
<< " --template Project template, currently: vue, game\n"
420461
<< " -d, --dir Base directory for project creation\n"
421462
<< " --force Overwrite existing directory\n\n"
422463

@@ -425,6 +466,11 @@ namespace vix::commands::NewCommand
425466
<< " vix build\n"
426467
<< " vix run\n\n"
427468

469+
<< "Game workflow\n"
470+
<< " cd mario/\n"
471+
<< " vix build\n"
472+
<< " vix run\n\n"
473+
428474
<< "Library workflow\n"
429475
<< " cd tree/\n"
430476
<< " vix build --build-target all\n"

src/commands/new/NewGenerator.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,50 @@ namespace vix::commands::new_cmd::generator
268268
return true;
269269
}
270270

271+
// ------------------------------------------------------------------
272+
// Game
273+
// ------------------------------------------------------------------
274+
275+
bool generate_game_project(
276+
const fs::path &projectDir,
277+
const std::string &projName,
278+
std::string &err)
279+
{
280+
const fs::path srcDir = projectDir / "src";
281+
const fs::path assetsDir = projectDir / "assets";
282+
283+
if (!ensure_dir(srcDir, err))
284+
return false;
285+
286+
if (!ensure_dir(assetsDir, err))
287+
return false;
288+
289+
if (!write_text_file(srcDir / "main.cpp",
290+
tpl::make_game_main_cpp(projName), err))
291+
return false;
292+
293+
if (!write_text_file(projectDir / "game.package.json",
294+
tpl::make_game_package_json(projName), err))
295+
return false;
296+
297+
if (!write_text_file(projectDir / "README.md",
298+
tpl::make_readme_game(projName), err))
299+
return false;
300+
301+
if (!write_text_file(projectDir / "vix.json",
302+
tpl::make_vix_json_game(projName), err))
303+
return false;
304+
305+
if (!write_text_file(projectDir / "vix.app",
306+
tpl::make_project_manifest_game(projName), err))
307+
return false;
308+
309+
if (!write_text_file(assetsDir / ".gitkeep", "", err))
310+
return false;
311+
312+
return true;
313+
}
314+
271315
// ------------------------------------------------------------------
272316
// Post-generation output
273317
// ------------------------------------------------------------------
@@ -294,4 +338,11 @@ namespace vix::commands::new_cmd::generator
294338
out::print_creation_lib(projectDir, projName);
295339
}
296340

341+
void print_next_steps_game(
342+
const fs::path &projectDir,
343+
const std::string &projName)
344+
{
345+
out::print_creation_game(projectDir, projName);
346+
}
347+
297348
} // namespace vix::commands::new_cmd::generator

src/commands/new/NewOutput.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ namespace vix::commands::new_cmd::output
9898
print_command_step(4, "vix tests", "run tests");
9999
}
100100

101+
static void print_steps_game(const std::string &projName)
102+
{
103+
section("next");
104+
105+
print_command_step(1, "cd " + projName + "/", "enter project");
106+
print_command_step(2, "vix build", "compile");
107+
print_command_step(3, "vix run", "start game");
108+
}
109+
101110
// ------------------------------------------------------------------
102111
// High-level entry points
103112
// ------------------------------------------------------------------
@@ -130,4 +139,13 @@ namespace vix::commands::new_cmd::output
130139
print_steps_lib(projName);
131140
}
132141

142+
void print_creation_game(
143+
const std::filesystem::path & /*projectDir*/,
144+
const std::string &projName)
145+
{
146+
print_banner(projName, "game");
147+
sep();
148+
print_steps_game(projName);
149+
}
150+
133151
} // namespace vix::commands::new_cmd::output

0 commit comments

Comments
 (0)