I think everything works now.

This commit is contained in:
Kapendev 2024-08-13 13:28:23 +03:00
parent d2d269b658
commit 4ccf2f9c89
29 changed files with 214 additions and 183 deletions

View file

@ -35,12 +35,12 @@ mixin addGameStart!(gameStart, 640, 360);
## Dependencies
To use Popka, you'll need the raylib library (version 5.0) installed on your system.
The [official raylib instructions](https://github.com/raysan5/raylib/wiki) will guide you through the process.
* [Joka](https://github.com/Kapendev/joka)
* [raylib](https://github.com/raysan5/raylib)
## Installation
This guide shows how to install Popka and its dependency, raylib, using DUB.
This guide shows how to install Popka and its dependencies using DUB.
While DUB simplifies the process, Popka itself doesn't require DUB.
Create a new folder and run inside the following commands:
@ -50,12 +50,11 @@ dub init -n
dub run popka:setup
```
The last line will download raylib and create some folders necessary for Popka to function properly. The folders:
The final line modifies the default app.d and dub.json files, downloads raylib, and creates the necessary folders for Popka to function properly. The following folders will be created:
* assets: This folder is used to store game assets such as images, sounds and fonts.
* assets: This folder is used to store game assets.
* web: This folder is used for exporting to the web.
The last line also changes the default app.d and dub.json files.
Once the installation is complete, you should be able to compile/run with:
```bash
@ -68,15 +67,16 @@ For more info about exporting to web, read [this](#web-support).
## Documentation
For an initial understanding, the [examples](examples) folder and the [engine.d](source/popka/game/engine.d) file can be a good starting point.
You can also read the [TOUR.md](TOUR.md) file for a more in-depth overview of the engine's functionalities.
You can also read the [TOUR.md](TOUR.md) file for a more in-depth overview.
## Attributes and BetterC Support
This project offers support for some attributes (`@safe`, `@nogc`, `nothrow`) and aims for good compatibility with BetterC.
If you encounter errors with BetterC, try using the `-i` flag.
## Web Support
For exporting to web, your project needs to be compatible with BetterC and it has to avoid some imports from the standard library.
For exporting to web, your project needs to be compatible with BetterC.
The [web](web) folder contains a helper script to assist with the web export process.
If you use DUB, you can run the script with:
@ -87,27 +87,24 @@ dub run popka:web
## raylib Bindings
Popka provides bindings for raylib that are compatible with BetterC and the web.
Additionally, it provides helper functions to reduce some boilerplate code.
All the helper functions are inside the [raylibpp.d](source/popka/vendor/ray/raylibpp.d) file.
```d
import popka.ray;
bool rayLoop() {
BeginDrawing();
ClearBackground(RAYWHITE);
DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
EndDrawing();
return false;
}
void rayStart(string path) {
InitWindow(800, 450, "raylib");
updateWindow!rayLoop();
int main() {
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window");
SetTargetFPS(60);
while (!WindowShouldClose()) {
BeginDrawing();
ClearBackground(RAYWHITE);
DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
EndDrawing();
}
CloseWindow();
return 0;
}
mixin addRayStart!rayStart;
```
## Note

94
TOUR.md
View file

@ -3,15 +3,16 @@
> [!WARNING]
> I am still working on this.
## Your First Popka Game
## Understanding the Code
Let's get started with Popka by creating a simple game that displays the classic message "Hello world!". Open your app.d file and paste the following code:
Let's get started with Popka by creating a simple game that displays the classic message "Hello world!".
Open your app.d file and paste the following code:
```d
import popka;
bool gameLoop() {
draw("Hello world!");
drawDebugText("Hello world!");
return false;
}
@ -23,38 +24,23 @@ void gameStart() {
mixin addGameStart!(gameStart, 640, 360);
```
Once you've saved the code, you should be able to compile/run with:
Let's see how everything works:
```bash
dub run
```
You should see a window with the text "Hello world!" displayed.
Congratulations, you've created your first Popka game!
### Understanding the Code
1. Import
```d
import popka;
```
This line imports some basic Popka modules that are needed to make your game.
2. Game Loop
1. Game Loop
```d
bool gameLoop() {
draw("Hello world!");
drawDebugText("Hello world!");
return false;
}
```
This function is the main loop of your game. It runs every frame, and on each frame in this example, it draws the message "Hello world!" on the game window.
The `return false` statement tells the game to keep running. If true is returned, then the game will stop running.
This function is the main loop of the game.
It runs every frame, and in this example, it draws the message "Hello world!" on the window.
The `return false` statement tells the game to keep running.
If true is returned, then the program will stop running.
3. Game Start and Mixin
2. Game Start
```d
void gameStart() {
@ -65,8 +51,18 @@ Congratulations, you've created your first Popka game!
mixin addGameStart!(gameStart, 640, 360);
```
This function is the starting point of your game. It runs only one time, and in this example, it locks the game resolution to 320 pixels wide and 180 pixels tall, and the `updateWindow!gameLoop()` call starts the game loop.
The `mixin addGameStart!(gameStart, 640, 360)` line might seem a bit complex right now, but it makes sure the `gameStart` function runs when your game starts and creates a game window that is 640 pixels wide and 360 pixels tall.
This function is the starting point of the game.
It runs only once, and in this example, it locks the game resolution to 320 pixels wide and 180 pixels tall.
The `updateWindow!gameLoop()` call starts the game loop.
3. Mixin
```d
mixin addGameStart!(gameStart, 640, 360)
```
The line makes sure the `gameStart` function runs when your game starts,
and in this example, it creates a game window that is 640 pixels wide and 360 pixels tall.
In essence, a Popka game typically relies on two key functions:
@ -75,34 +71,24 @@ In essence, a Popka game typically relies on two key functions:
## Drawing
Popka provides a set of drawing functions for creating various graphical elements.
While drawing is not pixel-perfect by default, you can enable pixel-perfect drawing by calling the `togglePixelPerfect()` function.
Popka provides a set of drawing functions.
While drawing is not pixel-perfect by default, you can enable pixel-perfect drawing by calling the `togglePixelPerfect` function.
```d
// Rectangle Drawing
void draw(Rect area, Color color = white);
// Basic Drawing Functions
void drawRect(Rect area, Color color = white);
void drawVec2(Vec2 point, float size, Color color = white);
void drawCirc(Circ area, Color color = white);
void drawLine(Line area, float size, Color color = white);
void drawTexture(Texture texture, Vec2 position, Rect area, DrawOptions options = DrawOptions());
void drawTexture(Texture texture, Vec2 position, DrawOptions options = DrawOptions());
void drawRune(Font font, Vec2 position, dchar rune, DrawOptions options = DrawOptions());
void drawText(Font font, Vec2 position, IStr text, DrawOptions options = DrawOptions());
void drawDebugText(IStr text, Vec2 position = Vec2(8.0f), DrawOptions options = DrawOptions());
// Circle Drawing
void draw(Circ area, Color color = white);
// Line Drawing
void draw(Line area, float size, Color color = white);
// Point Drawing
void draw(Vec2 point, float size, Color color = white);
// Sprite Drawing
void draw(Sprite sprite, Rect area, Vec2 position, DrawOptions options = DrawOptions());
void draw(Sprite sprite, Vec2 position, DrawOptions options = DrawOptions());
void draw(Sprite sprite, Vec2 tileSize, int tileID, Vec2 position, DrawOptions options = DrawOptions());
void draw(Sprite sprite, TileMap tileMap, Camera camera, Vec2 position, DrawOptions options = DrawOptions());
// Font Drawing
void draw(Font font, dchar rune, Vec2 position, DrawOptions options = DrawOptions());
void draw(Font font, const(char)[] text, Vec2 position, DrawOptions options = DrawOptions());
// Debug Drawing
void draw(const(char)[] text, Vec2 position = Vec2(8.0f), DrawOptions options = DrawOptions());
// Tile Map Drawing Functions
void drawTile(Texture texture, Vec2 position, int tileID, Vec2 tileSize, DrawOptions options = DrawOptions());
void drawTileMap(Texture texture, Vec2 position, TileMap tileMap, Camera camera, DrawOptions options = DrawOptions());
```
## Loading and Saving
@ -110,4 +96,4 @@ void draw(const(char)[] text, Vec2 position = Vec2(8.0f), DrawOptions options =
Functions that start with the word load/save will always try to read/write from/to the assets folder.
These functions handle both forward slashes and backslashes in file paths, ensuring compatibility across operating systems.
For instance, `loadText("levels/level5.txt")` and `loadText("levels\\level5.txt")` will function identically on any operating system.
Also, if you need text data for just a single frame, consider using the `loadTempText(const(char)[] path)` function.
Also, if you need text data for just a single frame, consider using the `loadTempText` function.

View file

@ -7,7 +7,7 @@
"license": "MIT",
"name": "popka",
"dependencies": {
"joka": "~main"
"joka": "*"
},
"subPackages" : [
"setup",

View file

@ -7,40 +7,26 @@
This example serves as a classic hello-world program, introducing the fundamental structure of a Popka program.
![image](hello.png)
## [Coins](coins.d)
This example shows how to create a simple game with Popka.
![image](coins.png)
## [Pong](pong.d)
This example shows how to create a pong-like game with Popka.
![image](pong.png)
## [Camera](camera.d)
This example shows how to use the camera structure of Popka.
![image](camera.png)
## [Dialogue](dialogue.d)
This example shows how to use the Popka dialogue system.
![image](dialogue.png)
## [Follower](follower.d)
This example shows how to create an animated character that follows the mouse.
![image](follower.png)
## [Map](map.d)
This example shows how to use the tile map structure of Popka.
![image](map.png)

View file

@ -1,6 +1,3 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// This example shows how to use the camera structure of Popka.
import popka;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -1,6 +1,3 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// This example shows how to create a simple game with Popka.
import popka;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -1,6 +1,3 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// This example shows how to use the Popka chat system.
import popka;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -1,3 +1,4 @@
/// This example shows how to create an animated character that follows the mouse.
import popka;
// The game variables.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -1,6 +1,3 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// This example serves as a classic hello-world program, introducing the fundamental structure of a Popka program.
import popka;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -1,6 +1,3 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// This example shows how to use the tile map structure of Popka.
import popka;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -1,6 +1,3 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
/// This example shows how to create a pong-like game with Popka.
import popka;
@ -41,7 +38,6 @@ bool gameLoop() {
// Move paddle1.
paddle1.position.y = clamp(paddle1.position.y + wasd.y * ballSpeed.y * deltaTime, paddle1.size.y * 0.5, resolution.y - paddle1.size.y * 0.5);
// Move paddle2.
auto paddle2Target = ball.position.y;
if (ballDirection.x < 1) {
@ -65,7 +61,6 @@ bool gameLoop() {
drawRect(ball.centerArea);
drawRect(paddle1.centerArea);
drawRect(paddle2.centerArea);
// Draw the counter.
auto textOptions = DrawOptions();
textOptions.scale = Vec2(2);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

59
scripts/header.d Executable file
View file

@ -0,0 +1,59 @@
#!/bin/env rdmd
/// A helper script that adds a header to every file in a directory.
import std;
enum fileExt = ".d";
enum header = "// ---
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/popka
// Version: v0.0.14
// ---";
int main(string[] args) {
if (args.length == 1) {
writeln("Provide a directory containing `%s` files.".format(fileExt));
return 0;
}
// Basic error checking.
auto targetDir = args[1];
if (!targetDir.exists) {
writeln("Path `%s` does not exist.".format(targetDir));
return 1;
}
auto tempIndex = header.countUntil("\n");
if (tempIndex == -1) {
writeln("Header separator does not exist.");
writeln("The first line of the header is the header separator.");
return 1;
}
auto headerSep = header[0 .. tempIndex];
// Add the header to the files.
foreach (file; dirEntries(targetDir, SpanMode.breadth).parallel) {
if (!file.name.endsWith(fileExt)) continue;
auto text = readText(file.name);
if (text.startsWith(headerSep)) {
foreach (i, c; text) {
if (i <= headerSep.length) continue;
if (i == text.length - headerSep.length) {
writeln("File `%s` does not have a second header separator.".format(file.name));
writeln("A header separator looks like this: `%s`".format(headerSep));
break;
}
if (text[i .. i + headerSep.length] == headerSep) {
std.file.write(file.name, header ~ text[i + headerSep.length .. $]);
break;
}
}
} else {
std.file.write(file.name, header ~ "\n\n" ~ text);
}
}
return 0;
}

View file

@ -1,29 +1,26 @@
#!/bin/env rdmd
// TODO: Needs some cleaning, but it works for now and I don't care.
/// A helper script that automates the project setup.
/// This script is designed with the idea that you use DUB, but it can work without DUB too.
import std.format;
import std.path;
import std.stdio;
import std.file;
import std.process;
// The config.
// ----------
enum noLibsArg = "offline";
enum dubFile = buildPath(".", "dub.json");
enum dubyFile = buildPath(".", "dub.selections.json");
enum appFile = buildPath(".", "source", "app.d");
enum gitignoreFile = buildPath(".", ".gitignore");
enum assetsDir = buildPath(".", "assets");
enum webDir = buildPath(".", "web");
// ----------
enum defaultDUBContent = `{
"name" : "game",
"description" : "A game made with Popka.",
@ -31,6 +28,7 @@ enum defaultDUBContent = `{
"copyright" : "Copyright © 2024, Name",
"license" : "proprietary",
"dependencies": {
"joka": "*",
"popka": "*"
},
"configurations": [
@ -73,7 +71,7 @@ enum defaultDUBContent = `{
"name": "web",
"targetType": "staticLibrary",
"targetName": "webgame",
"dflags": ["-mtriple=wasm32-unknown-unknown-wasm", "-checkaction=halt", "-betterC", "--release", "-i"]
"dflags": ["-mtriple=wasm32-unknown-unknown-wasm", "-checkaction=halt", "-betterC", "-i", "--release"]
}
]
}

View file

@ -1,5 +1,10 @@
// ---
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/popka
// Version: v0.0.14
// ---
/// The `chat` module provides a simple and versatile dialogue system.
module popka.chat;
@ -28,11 +33,23 @@ enum ChatUnitKind {
struct ChatUnit {
LStr text;
ChatUnitKind kind;
@safe @nogc nothrow:
void free() {
text.free();
}
}
struct ChatValue {
LStr name;
long value;
@safe @nogc nothrow:
void free() {
name.free();
}
}
alias ChatCommandRunner = void function(IStr[] args);
@ -73,27 +90,10 @@ struct Chat {
IStr[] choices() {
static IStr[16] buffer;
struct Range {
IStr menu;
bool empty() {
return menu.length == 0;
}
IStr front() {
auto temp = menu;
return temp.skipValue(ChatUnitKind.menu).trim();
}
void popFront() {
menu.skipValue(ChatUnitKind.menu);
}
}
auto length = 0;
auto range = hasChoices ? Range(units[unitIndex].text.items) : Range("");
foreach (item; range) {
buffer[length] = item;
auto temp = hasChoices ? units[unitIndex].text.items : "";
while (temp.length != 0) {
buffer[length] = temp.skipValue(ChatUnitKind.menu).trim();
length += 1;
}
return buffer[0 .. length];
@ -102,27 +102,10 @@ struct Chat {
IStr[] args() {
static IStr[16] buffer;
struct Range {
IStr command;
bool empty() {
return command.length == 0;
}
IStr front() {
auto temp = command;
return temp.skipValue(' ').trim();
}
void popFront() {
command.skipValue(' ');
}
}
auto length = 0;
auto range = hasArgs ? Range(units[unitIndex].text.items) : Range("");
foreach (item; range) {
buffer[length] = item;
auto temp = hasArgs ? units[unitIndex].text.items : "";
while (temp.length != 0) {
buffer[length] = temp.skipValue(' ').trim();
length += 1;
}
return buffer[0 .. length];
@ -134,7 +117,6 @@ struct Chat {
unitIndex = 0;
}
// TODO: Make it faster.
void jump(IStr point) {
if (point.length == 0) {
foreach (i; unitIndex + 1 .. units.length) {
@ -155,7 +137,6 @@ struct Chat {
}
}
// TODO: Make it faster.
void jump(Sz i) {
auto currPoint = 0;
foreach (j, unit; units.items) {
@ -214,7 +195,7 @@ struct Chat {
auto name = trim(skipValue(view, '='));
auto value = trim(skipValue(view, '='));
if (name.length == 0) {
assert(0, "TODO: An variable without a name is an error for now.");
assert(0, "TODO: A variable without a name is an error for now.");
}
// Find if variable exists.
foreach (i, variable; values.items) {
@ -244,7 +225,7 @@ struct Chat {
}
}
if (valueVariableIndex < 0) {
assert(0, "TODO: A variable that doesn't exist it an error for now.");
assert(0, "TODO: A variable that does not exist it an error for now.");
} else {
values[variableIndex].value = values[valueVariableIndex].value;
}
@ -267,7 +248,7 @@ struct Chat {
}
// Add/Remove from variable.
if (variableIndex < 0) {
assert(0, "TODO: A variable that doesn't exist it an error for now.");
assert(0, "TODO: A variable that does not exist it an error for now.");
}
if (units[unitIndex].kind == ChatUnitKind.plus) {
values[variableIndex].value += 1;
@ -319,11 +300,11 @@ struct Chat {
void clear() {
foreach (ref unit; units) {
unit.text.free();
unit.free();
}
units.clear();
foreach (ref variable; values) {
variable.name.free();
variable.free();
}
values.clear();
reset();
@ -331,11 +312,11 @@ struct Chat {
void free() {
foreach (ref unit; units) {
unit.text.free();
unit.free();
}
units.free();
foreach (ref variable; values) {
variable.name.free();
variable.free();
}
values.free();
reset();

View file

@ -1,5 +1,10 @@
// ---
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/popka
// Version: v0.0.14
// ---
/// The `engine` module functions as a lightweight 2D game engine.
module popka.engine;
@ -676,18 +681,6 @@ void drawTexture(Texture texture, Vec2 position, DrawOptions options = DrawOptio
drawTexture(texture, position, Rect(texture.size), options);
}
void drawTile(Texture texture, Vec2 position, int tileID, Vec2 tileSize, DrawOptions options = DrawOptions()) {
auto gridWidth = cast(int) (texture.size.x / tileSize.x);
auto gridHeight = cast(int) (texture.size.y / tileSize.y);
if (gridWidth == 0 || gridHeight == 0) {
return;
}
auto row = tileID / gridWidth;
auto col = tileID % gridWidth;
auto area = Rect(col * tileSize.x, row * tileSize.y, tileSize.x, tileSize.y);
drawTexture(texture, position, area, options);
}
@trusted
void drawRune(Font font, Vec2 position, dchar rune, DrawOptions options = DrawOptions()) {
if (font.isEmpty) {

View file

@ -1,6 +1,12 @@
// ---
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/popka
// Version: v0.0.14
// ---
// The `popka`` module imports all of its submodules.
module popka;
public import popka.chat;

View file

@ -1,6 +1,12 @@
// ---
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/popka
// Version: v0.0.14
// ---
/// The `ray` module provides access to the raylib library.
module popka.ray;
public import popka.ray.raylib;

View file

@ -1,3 +1,11 @@
// ---
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/popka
// Version: v0.0.14
// ---
/**********************************************************************************************
*
* raylib v5.0 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
@ -76,6 +84,7 @@
*
**********************************************************************************************/
/// The `rayib` module provides access to the raylib.h functions.
module popka.ray.raylib;
// import core.stdc.config;

View file

@ -1,3 +1,11 @@
// ---
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/popka
// Version: v0.0.14
// ---
/**********************************************************************************************
*
* rlgl v4.5 - A multi-OpenGL abstraction layer with an immediate-mode style API
@ -104,6 +112,7 @@
*
**********************************************************************************************/
/// The `rlgl` module provides access to the rlgl.h functions.
module popka.ray.rlgl;
@nogc nothrow extern(C):

View file

@ -1,5 +1,10 @@
// ---
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/popka
// Version: v0.0.14
// ---
/// The `tilemap` module provides a simple and fast tile map.
module popka.tilemap;
@ -86,6 +91,18 @@ Result!TileMap loadTileMap(IStr path) {
return toTileMap(temp.unwrap());
}
void drawTile(Texture texture, Vec2 position, int tileID, Vec2 tileSize, DrawOptions options = DrawOptions()) {
auto gridWidth = cast(int) (texture.size.x / tileSize.x);
auto gridHeight = cast(int) (texture.size.y / tileSize.y);
if (gridWidth == 0 || gridHeight == 0) {
return;
}
auto row = tileID / gridWidth;
auto col = tileID % gridWidth;
auto area = Rect(col * tileSize.x, row * tileSize.y, tileSize.x, tileSize.y);
drawTexture(texture, position, area, options);
}
void drawTileMap(Texture texture, Vec2 position, TileMap tileMap, Camera camera, DrawOptions options = DrawOptions()) {
enum extraTileCount = 1;

View file

@ -1,5 +1,10 @@
// ---
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/popka
// Version: v0.0.14
// ---
/// The `types` module defines all the types used within the `engine` module.
module popka.types;

View file

@ -1,9 +1,10 @@
#!/bin/env rdmd
// TODO: Needs some cleaning, but it works for now and I don't care.
/// A helper script to assist with the web export process.
/// This script is designed with the idea that Popka is included inside your source folder, but it can work without Popka too.
/// Just copy-paste the web folder into your project and run the script from your project folder.
import std.string;
import std.format;
import std.path;
@ -11,7 +12,6 @@ import std.stdio;
import std.file;
import std.process;
// The config.
// ----------
enum dflags = "-betterC -O --release"; // The compiler flags passed to ldc. Local dependencies can be added here.
@ -33,7 +33,6 @@ enum dubWebConfig = "web"; // The config
enum dubLibName = "webgame"; // The library name that DUB will output with the web config.
// ----------
// This is used if a shell file does not exist.
enum defaultShellContent = `<!doctype html>
<html lang="EN-us">
@ -116,7 +115,6 @@ enum defaultShellContent = `<!doctype html>
</html>
`;
/// Check if path exists and print an error message if needed.
bool check(const(char)[] path, bool isLoud = true) {
if (!exists(path)) {