mirror of
https://github.com/Kapendev/parin.git
synced 2025-04-26 21:19:56 +03:00
Fixed setup bug and changed chat to dialogue again.
This commit is contained in:
parent
4ccf2f9c89
commit
fbd84bf531
19 changed files with 159 additions and 153 deletions
15
README.md
15
README.md
|
@ -16,7 +16,7 @@ void gameStart() {
|
|||
updateWindow!gameLoop();
|
||||
}
|
||||
|
||||
mixin addGameStart!(gameStart, 640, 360);
|
||||
mixin callGameStart!(gameStart, 640, 360);
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
|
@ -55,19 +55,19 @@ The final line modifies the default app.d and dub.json files, downloads raylib,
|
|||
* assets: This folder is used to store game assets.
|
||||
* web: This folder is used for exporting to the web.
|
||||
|
||||
Once the installation is complete, you should be able to compile/run with:
|
||||
Once the installation is complete, run the following command:
|
||||
|
||||
```bash
|
||||
dub run
|
||||
```
|
||||
|
||||
You can pass `offline` to the script if you don't want to download raylib.
|
||||
For more info about exporting to web, read [this](#web-support).
|
||||
If everything is set up correctly, a window will appear showing the message "Hello world!".
|
||||
To avoid downloading raylib, pass `offline` to the script.
|
||||
|
||||
## 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.
|
||||
For an initial understanding, the [examples](examples) folder can be a good starting point.
|
||||
For a more detailed overview, check the [TOUR.md](TOUR.md) file.
|
||||
|
||||
## Attributes and BetterC Support
|
||||
|
||||
|
@ -76,9 +76,8 @@ 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.
|
||||
To export a game to the web, the game must 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:
|
||||
|
||||
```bash
|
||||
dub run popka:web
|
||||
|
|
49
TOUR.md
49
TOUR.md
|
@ -1,12 +1,8 @@
|
|||
# Tour
|
||||
|
||||
> [!WARNING]
|
||||
> I am still working on this.
|
||||
# Tour (WIP)
|
||||
|
||||
## 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:
|
||||
To begin, open the main file of your project and copy-paste the following code:
|
||||
|
||||
```d
|
||||
import popka;
|
||||
|
@ -21,10 +17,11 @@ void gameStart() {
|
|||
updateWindow!gameLoop();
|
||||
}
|
||||
|
||||
mixin addGameStart!(gameStart, 640, 360);
|
||||
mixin callGameStart!(gameStart, 640, 360);
|
||||
```
|
||||
|
||||
Let's see how everything works:
|
||||
This code will create a window that displays the message "Hello world!".
|
||||
Here is a breakdown of how this code works:
|
||||
|
||||
1. Game Loop
|
||||
|
||||
|
@ -36,9 +33,9 @@ Let's see how everything works:
|
|||
```
|
||||
|
||||
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.
|
||||
It runs every frame and, in this example, draws the message "Hello world!".
|
||||
The `return false` statement indicates that the game should continue running.
|
||||
If `true` were returned, the game would stop.
|
||||
|
||||
2. Game Start
|
||||
|
||||
|
@ -47,27 +44,25 @@ Let's see how everything works:
|
|||
lockResolution(320, 180);
|
||||
updateWindow!gameLoop();
|
||||
}
|
||||
|
||||
mixin addGameStart!(gameStart, 640, 360);
|
||||
```
|
||||
|
||||
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.
|
||||
It runs only once and, in this example, locks the game resolution to 320 pixels wide and 180 pixels tall.
|
||||
The `updateWindow!gameLoop` call starts the main game loop.
|
||||
|
||||
3. Mixin
|
||||
|
||||
```d
|
||||
mixin addGameStart!(gameStart, 640, 360)
|
||||
mixin callGameStart!(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.
|
||||
This line sets up the `gameStart` function to run when the game starts
|
||||
and, in this example, creates a game window that is 640 pixels wide and 360 pixels tall.
|
||||
|
||||
In essence, a Popka game typically relies on two key functions:
|
||||
|
||||
* A game loop function.
|
||||
* A game start function.
|
||||
* A loop function.
|
||||
* A start function.
|
||||
|
||||
## Drawing
|
||||
|
||||
|
@ -75,18 +70,18 @@ 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
|
||||
// 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());
|
||||
|
||||
// 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());
|
||||
```
|
||||
|
@ -95,5 +90,11 @@ void drawTileMap(Texture texture, Vec2 position, TileMap tileMap, Camera camera,
|
|||
|
||||
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` function.
|
||||
|
||||
```d
|
||||
loadText("levels/level5.txt");
|
||||
loadText("levels\\level5.txt");
|
||||
```
|
||||
|
||||
Both of these calls will function identically on any operating system.
|
||||
Also, if text is needed for only a single frame, use the `loadTempText` function.
|
||||
|
|
|
@ -30,4 +30,4 @@ void gameStart() {
|
|||
updateWindow!gameLoop();
|
||||
}
|
||||
|
||||
mixin addGameStart!(gameStart, 640, 360);
|
||||
mixin callGameStart!(gameStart, 640, 360);
|
||||
|
|
|
@ -66,4 +66,4 @@ void gameStart() {
|
|||
updateWindow!gameLoop();
|
||||
}
|
||||
|
||||
mixin addGameStart!(gameStart, 640, 360);
|
||||
mixin callGameStart!(gameStart, 640, 360);
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
/// This example shows how to use the Popka chat system.
|
||||
/// This example shows how to use the Popka dialogue system.
|
||||
import popka;
|
||||
|
||||
// The game variables.
|
||||
auto chat = Chat();
|
||||
auto dialogue = Dialogue();
|
||||
auto script = "
|
||||
# This is a comment.
|
||||
|
||||
! choiceCount
|
||||
|
||||
* menuPoint
|
||||
^ Select first choice. ^ Select second choice. ^ End chat.
|
||||
^ Select first choice. ^ Select second choice. ^ End dialogue.
|
||||
|
||||
* choice1
|
||||
> Bob
|
||||
|
@ -34,34 +34,34 @@ auto script = "
|
|||
|
||||
bool gameLoop() {
|
||||
// Update the game.
|
||||
if (chat.canUpdate) {
|
||||
if (chat.hasChoices) {
|
||||
auto keys = digitChars[1 .. 1 + chat.choices.length];
|
||||
if (dialogue.canUpdate) {
|
||||
if (dialogue.hasChoices) {
|
||||
auto keys = digitChars[1 .. 1 + dialogue.choices.length];
|
||||
foreach (i, key; keys) {
|
||||
if (key.isPressed) {
|
||||
chat.pick(i);
|
||||
dialogue.pick(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (Keyboard.space.isPressed) {
|
||||
chat.update();
|
||||
dialogue.update();
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the chat.
|
||||
if (chat.hasChoices) {
|
||||
foreach (i, choice; chat.choices) {
|
||||
// Draw the dialogue.
|
||||
if (dialogue.hasChoices) {
|
||||
foreach (i, choice; dialogue.choices) {
|
||||
auto choicePosition = Vec2(8, 8 + i * 14);
|
||||
drawDebugText("{}".format(i + 1), choicePosition);
|
||||
drawDebugText(" | {}".format(choice), choicePosition);
|
||||
}
|
||||
} else if (chat.canUpdate) {
|
||||
drawDebugText("{}: {}".format(chat.actor, chat.text));
|
||||
} else if (dialogue.canUpdate) {
|
||||
drawDebugText("{}: {}".format(dialogue.actor, dialogue.text));
|
||||
} else {
|
||||
drawDebugText("The chat has ended.");
|
||||
drawDebugText("The dialogue has ended.");
|
||||
}
|
||||
|
||||
// Draw the game info/
|
||||
// Draw the game info.
|
||||
auto infoPosition = Vec2(8, resolution.y - 2 - 14 * 2);
|
||||
drawRect(Rect(0, resolution.y * 0.8, resolution.x, resolution.y), gray1);
|
||||
drawDebugText("Press 1, 2 or 3 to select a choice.", infoPosition);
|
||||
|
@ -71,11 +71,11 @@ bool gameLoop() {
|
|||
|
||||
void gameStart() {
|
||||
lockResolution(320, 180);
|
||||
// Parse the chat script of the game.
|
||||
// The first update makes the chat go to the first available line.
|
||||
chat.parse(script);
|
||||
chat.update();
|
||||
// Parse the dialogue script of the game.
|
||||
// The first update makes the dialogue go to the first available line.
|
||||
dialogue.parse(script);
|
||||
dialogue.update();
|
||||
updateWindow!gameLoop();
|
||||
}
|
||||
|
||||
mixin addGameStart!(gameStart, 640, 360);
|
||||
mixin callGameStart!(gameStart, 640, 360);
|
||||
|
|
|
@ -56,4 +56,4 @@ void gameStart() {
|
|||
atlas.free();
|
||||
}
|
||||
|
||||
mixin addGameStart!(gameStart, 640, 360);
|
||||
mixin callGameStart!(gameStart, 640, 360);
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
/// This example serves as a classic hello-world program, introducing the fundamental structure of a Popka program.
|
||||
import popka;
|
||||
|
||||
// The game loop. This is called every frame.
|
||||
// If true is returned, then the game will stop running.
|
||||
// The loop function. This is called every frame.
|
||||
// If true is returned, then the game will stop.
|
||||
bool gameLoop() {
|
||||
drawDebugText("Hello world!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// The game start. This is one time.
|
||||
// The start function. This is called once.
|
||||
void gameStart() {
|
||||
lockResolution(320, 180);
|
||||
updateWindow!gameLoop();
|
||||
}
|
||||
|
||||
// Creates a main function that calls the given function and creates a game window that is 640 pixels wide and 360 pixels tall.
|
||||
mixin addGameStart!(gameStart, 640, 360);
|
||||
mixin callGameStart!(gameStart, 640, 360);
|
||||
|
|
|
@ -41,4 +41,4 @@ void gameStart() {
|
|||
atlas.free();
|
||||
}
|
||||
|
||||
mixin addGameStart!(gameStart, 640, 360);
|
||||
mixin callGameStart!(gameStart, 640, 360);
|
||||
|
|
|
@ -79,4 +79,4 @@ void gameStart() {
|
|||
updateWindow!gameLoop();
|
||||
}
|
||||
|
||||
mixin addGameStart!(gameStart, 640, 360);
|
||||
mixin callGameStart!(gameStart, 640, 360);
|
||||
|
|
|
@ -10,7 +10,7 @@ enum header = "// ---
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Email: alexandroskapretsos@gmail.com
|
||||
// Project: https://github.com/Kapendev/popka
|
||||
// Version: v0.0.14
|
||||
// Version: v0.0.15
|
||||
// ---";
|
||||
|
||||
int main(string[] args) {
|
||||
|
|
|
@ -96,7 +96,7 @@ lib*
|
|||
enum defaultAppContent = `import popka;
|
||||
|
||||
bool gameLoop() {
|
||||
draw("Hello world!");
|
||||
drawDebugText("Hello world!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ void gameStart() {
|
|||
updateWindow!gameLoop();
|
||||
}
|
||||
|
||||
mixin addGameStart!(gameStart, 640, 360);
|
||||
mixin callGameStart!(gameStart, 640, 360);
|
||||
`;
|
||||
|
||||
/// Check if path exists and print an error message if needed.
|
||||
|
|
|
@ -3,20 +3,20 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Email: alexandroskapretsos@gmail.com
|
||||
// Project: https://github.com/Kapendev/popka
|
||||
// Version: v0.0.14
|
||||
// Version: v0.0.15
|
||||
// ---
|
||||
|
||||
/// The `chat` module provides a simple and versatile dialogue system.
|
||||
module popka.chat;
|
||||
/// The `dialogue` module provides a simple and versatile dialogue system.
|
||||
module popka.dialogue;
|
||||
|
||||
import popka.engine;
|
||||
public import joka;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
enum ChatUnitKindChars = ".#*@>|^!+-$";
|
||||
enum DialogueUnitKindChars = ".#*@>|^!+-$";
|
||||
|
||||
enum ChatUnitKind {
|
||||
enum DialogueUnitKind {
|
||||
pause = '.',
|
||||
comment = '#',
|
||||
point = '*',
|
||||
|
@ -30,18 +30,19 @@ enum ChatUnitKind {
|
|||
command = '$',
|
||||
}
|
||||
|
||||
struct ChatUnit {
|
||||
struct DialogueUnit {
|
||||
LStr text;
|
||||
ChatUnitKind kind;
|
||||
DialogueUnitKind kind;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
void free() {
|
||||
text.free();
|
||||
this = DialogueUnit();
|
||||
}
|
||||
}
|
||||
|
||||
struct ChatValue {
|
||||
struct DialogueValue {
|
||||
LStr name;
|
||||
long value;
|
||||
|
||||
|
@ -49,14 +50,15 @@ struct ChatValue {
|
|||
|
||||
void free() {
|
||||
name.free();
|
||||
this = DialogueValue();
|
||||
}
|
||||
}
|
||||
|
||||
alias ChatCommandRunner = void function(IStr[] args);
|
||||
alias DialogueCommandRunner = void function(IStr[] args);
|
||||
|
||||
struct Chat {
|
||||
List!ChatUnit units;
|
||||
List!ChatValue values;
|
||||
struct Dialogue {
|
||||
List!DialogueUnit units;
|
||||
List!DialogueValue values;
|
||||
IStr text;
|
||||
IStr actor;
|
||||
Sz unitIndex;
|
||||
|
@ -67,20 +69,20 @@ struct Chat {
|
|||
return units.length == 0;
|
||||
}
|
||||
|
||||
bool isKind(ChatUnitKind kind) {
|
||||
bool isKind(DialogueUnitKind kind) {
|
||||
return unitIndex < units.length && units[unitIndex].kind == kind;
|
||||
}
|
||||
|
||||
bool hasChoices() {
|
||||
return isKind(ChatUnitKind.menu);
|
||||
return isKind(DialogueUnitKind.menu);
|
||||
}
|
||||
|
||||
bool hasArgs() {
|
||||
return isKind(ChatUnitKind.command);
|
||||
return isKind(DialogueUnitKind.command);
|
||||
}
|
||||
|
||||
bool hasEnded() {
|
||||
return isKind(ChatUnitKind.pause);
|
||||
return isKind(DialogueUnitKind.pause);
|
||||
}
|
||||
|
||||
bool canUpdate() {
|
||||
|
@ -93,7 +95,7 @@ struct Chat {
|
|||
auto length = 0;
|
||||
auto temp = hasChoices ? units[unitIndex].text.items : "";
|
||||
while (temp.length != 0) {
|
||||
buffer[length] = temp.skipValue(ChatUnitKind.menu).trim();
|
||||
buffer[length] = temp.skipValue(DialogueUnitKind.menu).trim();
|
||||
length += 1;
|
||||
}
|
||||
return buffer[0 .. length];
|
||||
|
@ -121,7 +123,7 @@ struct Chat {
|
|||
if (point.length == 0) {
|
||||
foreach (i; unitIndex + 1 .. units.length) {
|
||||
auto unit = units[i];
|
||||
if (unit.kind == ChatUnitKind.point) {
|
||||
if (unit.kind == DialogueUnitKind.point) {
|
||||
unitIndex = i;
|
||||
break;
|
||||
}
|
||||
|
@ -129,7 +131,7 @@ struct Chat {
|
|||
} else {
|
||||
foreach (i; 0 .. units.length) {
|
||||
auto unit = units[i];
|
||||
if (unit.kind == ChatUnitKind.point && unit.text.items == point) {
|
||||
if (unit.kind == DialogueUnitKind.point && unit.text.items == point) {
|
||||
unitIndex = i;
|
||||
break;
|
||||
}
|
||||
|
@ -140,7 +142,7 @@ struct Chat {
|
|||
void jump(Sz i) {
|
||||
auto currPoint = 0;
|
||||
foreach (j, unit; units.items) {
|
||||
if (unit.kind == ChatUnitKind.point) {
|
||||
if (unit.kind == DialogueUnitKind.point) {
|
||||
if (currPoint == i) {
|
||||
unitIndex = j;
|
||||
break;
|
||||
|
@ -161,7 +163,7 @@ struct Chat {
|
|||
update();
|
||||
}
|
||||
|
||||
void run(ChatCommandRunner runner) {
|
||||
void run(DialogueCommandRunner runner) {
|
||||
runner(args);
|
||||
update();
|
||||
}
|
||||
|
@ -172,24 +174,24 @@ struct Chat {
|
|||
unitIndex += 1;
|
||||
text = units[unitIndex].text.items;
|
||||
final switch (units[unitIndex].kind) {
|
||||
case ChatUnitKind.line, ChatUnitKind.menu, ChatUnitKind.command, ChatUnitKind.pause: {
|
||||
case DialogueUnitKind.line, DialogueUnitKind.menu, DialogueUnitKind.command, DialogueUnitKind.pause: {
|
||||
break;
|
||||
}
|
||||
case ChatUnitKind.comment, ChatUnitKind.point: {
|
||||
case DialogueUnitKind.comment, DialogueUnitKind.point: {
|
||||
update();
|
||||
break;
|
||||
}
|
||||
case ChatUnitKind.actor: {
|
||||
case DialogueUnitKind.actor: {
|
||||
actor = text;
|
||||
update();
|
||||
break;
|
||||
}
|
||||
case ChatUnitKind.jump: {
|
||||
case DialogueUnitKind.jump: {
|
||||
jump(text);
|
||||
update();
|
||||
break;
|
||||
}
|
||||
case ChatUnitKind.variable: {
|
||||
case DialogueUnitKind.variable: {
|
||||
auto variableIndex = -1;
|
||||
auto view = text;
|
||||
auto name = trim(skipValue(view, '='));
|
||||
|
@ -206,7 +208,7 @@ struct Chat {
|
|||
}
|
||||
// Create variable if it does not exist.
|
||||
if (variableIndex < 0) {
|
||||
auto variable = ChatValue();
|
||||
auto variable = DialogueValue();
|
||||
variable.name.append(name);
|
||||
values.append(variable);
|
||||
variableIndex = cast(int) values.length - 1;
|
||||
|
@ -236,7 +238,7 @@ struct Chat {
|
|||
update();
|
||||
break;
|
||||
}
|
||||
case ChatUnitKind.plus, ChatUnitKind.minus: {
|
||||
case DialogueUnitKind.plus, DialogueUnitKind.minus: {
|
||||
auto variableIndex = -1;
|
||||
auto name = text;
|
||||
// Find if variable exists.
|
||||
|
@ -250,7 +252,7 @@ struct Chat {
|
|||
if (variableIndex < 0) {
|
||||
assert(0, "TODO: A variable that does not exist it an error for now.");
|
||||
}
|
||||
if (units[unitIndex].kind == ChatUnitKind.plus) {
|
||||
if (units[unitIndex].kind == DialogueUnitKind.plus) {
|
||||
values[variableIndex].value += 1;
|
||||
} else {
|
||||
values[variableIndex].value -= 1;
|
||||
|
@ -268,7 +270,7 @@ struct Chat {
|
|||
return Fault.invalid;
|
||||
}
|
||||
|
||||
units.append(ChatUnit(LStr(), ChatUnitKind.pause));
|
||||
units.append(DialogueUnit(LStr(), DialogueUnitKind.pause));
|
||||
auto isFirstLine = true;
|
||||
auto view = script;
|
||||
while (view.length != 0) {
|
||||
|
@ -280,20 +282,20 @@ struct Chat {
|
|||
auto kind = line[0];
|
||||
if (isFirstLine) {
|
||||
isFirstLine = false;
|
||||
if (kind == ChatUnitKind.pause) {
|
||||
if (kind == DialogueUnitKind.pause) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (isValidChatUnitKind(kind)) {
|
||||
auto realKind = cast(ChatUnitKind) kind;
|
||||
units.append(ChatUnit(LStr(text), realKind));
|
||||
if (isValidDialogueUnitKind(kind)) {
|
||||
auto realKind = cast(DialogueUnitKind) kind;
|
||||
units.append(DialogueUnit(LStr(text), realKind));
|
||||
} else {
|
||||
clear();
|
||||
return Fault.invalid;
|
||||
}
|
||||
}
|
||||
if (units.items[$ - 1].kind != ChatUnitKind.pause) {
|
||||
units.append(ChatUnit(LStr(), ChatUnitKind.pause));
|
||||
if (units.items[$ - 1].kind != DialogueUnitKind.pause) {
|
||||
units.append(DialogueUnit(LStr(), DialogueUnitKind.pause));
|
||||
}
|
||||
return Fault.none;
|
||||
}
|
||||
|
@ -323,8 +325,8 @@ struct Chat {
|
|||
}
|
||||
}
|
||||
|
||||
bool isValidChatUnitKind(char c) {
|
||||
foreach (kind; ChatUnitKindChars) {
|
||||
bool isValidDialogueUnitKind(char c) {
|
||||
foreach (kind; DialogueUnitKindChars) {
|
||||
if (c == kind) {
|
||||
return true;
|
||||
}
|
||||
|
@ -332,19 +334,19 @@ bool isValidChatUnitKind(char c) {
|
|||
return false;
|
||||
}
|
||||
|
||||
Result!Chat toChat(IStr script) {
|
||||
auto value = Chat();
|
||||
Result!Dialogue toDialogue(IStr script) {
|
||||
auto value = Dialogue();
|
||||
auto fault = value.parse(script);
|
||||
if (fault) {
|
||||
value.free();
|
||||
}
|
||||
return Result!Chat(value, fault);
|
||||
return Result!Dialogue(value, fault);
|
||||
}
|
||||
|
||||
Result!Chat loadChat(IStr path) {
|
||||
Result!Dialogue loadDialogue(IStr path) {
|
||||
auto temp = loadTempText(path);
|
||||
if (temp.isNone) {
|
||||
return Result!Chat(temp.fault);
|
||||
return Result!Dialogue(temp.fault);
|
||||
}
|
||||
return toChat(temp.unwrap());
|
||||
return toDialogue(temp.unwrap());
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Email: alexandroskapretsos@gmail.com
|
||||
// Project: https://github.com/Kapendev/popka
|
||||
// Version: v0.0.14
|
||||
// Version: v0.0.15
|
||||
// ---
|
||||
|
||||
/// The `engine` module functions as a lightweight 2D game engine.
|
||||
|
@ -25,7 +25,7 @@ ray.Camera2D _toRay(Camera camera) {
|
|||
);
|
||||
}
|
||||
|
||||
/// Returns a random integer between 0 and float.max (inclusive).
|
||||
/// Returns a random integer between 0 and int.max (inclusive).
|
||||
@trusted
|
||||
int randi() {
|
||||
return ray.GetRandomValue(0, int.max);
|
||||
|
@ -37,7 +37,7 @@ float randf() {
|
|||
return ray.GetRandomValue(0, cast(int) float.max) / cast(float) cast(int) float.max;
|
||||
}
|
||||
|
||||
/// Sets the seed for the random number generator to something specific.
|
||||
/// Sets the seed of the random number generator to the given value.
|
||||
@trusted
|
||||
void randomize(int seed) {
|
||||
ray.SetRandomSeed(seed);
|
||||
|
@ -48,19 +48,19 @@ void randomize() {
|
|||
randomize(randi);
|
||||
}
|
||||
|
||||
/// Converts a world point to a screen point based on the given camera.
|
||||
/// Converts a world position to a screen position based on the given camera.
|
||||
@trusted
|
||||
Vec2 toScreenPosition(Vec2 point, Camera camera) {
|
||||
return toPopka(ray.GetWorldToScreen2D(point.toRay(), camera._toRay()));
|
||||
Vec2 toScreenPosition(Vec2 position, Camera camera) {
|
||||
return toPopka(ray.GetWorldToScreen2D(position.toRay(), camera._toRay()));
|
||||
}
|
||||
|
||||
/// Converts a screen point to a world point based on the given camera.
|
||||
/// Converts a screen position to a world position based on the given camera.
|
||||
@trusted
|
||||
Vec2 toWorldPosition(Vec2 point, Camera camera) {
|
||||
return toPopka(ray.GetScreenToWorld2D(point.toRay(), camera._toRay()));
|
||||
Vec2 toWorldPosition(Vec2 position, Camera camera) {
|
||||
return toPopka(ray.GetScreenToWorld2D(position.toRay(), camera._toRay()));
|
||||
}
|
||||
|
||||
/// Returns the default font. This font should not be freed.
|
||||
/// Returns the default Popka font. This font should not be freed.
|
||||
@trusted
|
||||
Font dfltFont() {
|
||||
auto result = ray.GetFontDefault().toPopka();
|
||||
|
@ -69,6 +69,7 @@ Font dfltFont() {
|
|||
return result;
|
||||
}
|
||||
|
||||
/// Returns an absolute path to the assets folder.
|
||||
IStr assetsPath() {
|
||||
return engineState.assetsPath.items;
|
||||
}
|
||||
|
@ -78,21 +79,21 @@ IStr toAssetsPath(IStr path) {
|
|||
}
|
||||
|
||||
/// Loads a text file from the assets folder and returns its contents as a list.
|
||||
/// Can handle both forward slashes and backslashes in file paths, ensuring compatibility across operating systems.
|
||||
/// Can handle both forward slashes and backslashes in file paths.
|
||||
Result!LStr loadText(IStr path) {
|
||||
return readText(path.toAssetsPath());
|
||||
}
|
||||
|
||||
/// Loads a text file from the assets folder and returns its contents as a slice.
|
||||
/// The slice can be safely used until this function is called again.
|
||||
/// Can handle both forward slashes and backslashes in file paths, ensuring compatibility across operating systems.
|
||||
/// Can handle both forward slashes and backslashes in file paths.
|
||||
Result!IStr loadTempText(IStr path) {
|
||||
auto fault = readTextIntoBuffer(path.toAssetsPath(), engineState.tempText);
|
||||
return Result!IStr(engineState.tempText.items, fault);
|
||||
}
|
||||
|
||||
/// Loads an image file from the assets folder.
|
||||
/// Can handle both forward slashes and backslashes in file paths, ensuring compatibility across operating systems.
|
||||
/// Loads an image file (PNG) from the assets folder.
|
||||
/// Can handle both forward slashes and backslashes in file paths.
|
||||
@trusted
|
||||
Result!Texture loadTexture(IStr path) {
|
||||
auto value = ray.LoadTexture(path.toAssetsPath().toCStr().unwrapOr()).toPopka();
|
||||
|
@ -106,20 +107,21 @@ Result!Viewport loadViewport(int width, int height) {
|
|||
}
|
||||
|
||||
@trusted
|
||||
/// Loads a font file (TTF) from the assets folder.
|
||||
/// Can handle both forward slashes and backslashes in file paths.
|
||||
Result!Font loadFont(IStr path, uint size, const(dchar)[] runes = []) {
|
||||
auto value = ray.LoadFontEx(path.toAssetsPath.toCStr().unwrapOr(), size, cast(int*) runes.ptr, cast(int) runes.length).toPopka();
|
||||
return Result!Font(value, value.isEmpty.toFault(Fault.cantFind));
|
||||
}
|
||||
|
||||
/// Saves a text file to the assets folder.
|
||||
/// Can handle both forward slashes and backslashes in file paths, ensuring compatibility across operating systems.
|
||||
/// Can handle both forward slashes and backslashes in file paths.
|
||||
Fault saveText(IStr path, IStr text) {
|
||||
return writeText(path.toAssetsPath(), text);
|
||||
}
|
||||
|
||||
/// Opens the game window with the given size and title.
|
||||
/// This function does not work if the window is already open, because Popka only works with one window.
|
||||
/// Usually you should avoid calling this function manually.
|
||||
/// Opens a window with the given size and title.
|
||||
/// You should avoid calling this function manually.
|
||||
@trusted
|
||||
void openWindow(int width, int height, IStr title = "Popka") {
|
||||
if (ray.IsWindowReady) {
|
||||
|
@ -135,7 +137,7 @@ void openWindow(int width, int height, IStr title = "Popka") {
|
|||
engineState.fullscreenState.lastWindowSize = Vec2(width, height);
|
||||
}
|
||||
|
||||
/// Updates the game window every frame with the specified loop function.
|
||||
/// Updates the window every frame with the given loop function.
|
||||
/// This function will return when the loop function returns true.
|
||||
@trusted
|
||||
void updateWindow(alias loopFunc)() {
|
||||
|
@ -229,52 +231,49 @@ void updateWindow(alias loopFunc)() {
|
|||
}
|
||||
}
|
||||
|
||||
/// Closes the game window.
|
||||
/// Usually you should avoid calling this function manually.
|
||||
/// Closes the window.
|
||||
/// You should avoid calling this function manually.
|
||||
@trusted
|
||||
void closeWindow() {
|
||||
if (!ray.IsWindowReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
engineState.tempText.free();
|
||||
engineState.assetsPath.free();
|
||||
engineState.viewport.free();
|
||||
|
||||
engineState.free();
|
||||
ray.CloseAudioDevice();
|
||||
ray.CloseWindow();
|
||||
engineState = EngineState();
|
||||
}
|
||||
|
||||
/// Sets the window background color to the given color.
|
||||
void setBackgroundColor(Color color) {
|
||||
engineState.backgroundColor = color;
|
||||
}
|
||||
|
||||
/// Returns true if the FPS of the game is locked.
|
||||
/// Returns true if the FPS is locked.
|
||||
bool isFpsLocked() {
|
||||
return engineState.flags.isFpsLocked;
|
||||
}
|
||||
|
||||
/// Locks the FPS of the game to a specific value.
|
||||
/// Locks the FPS to the given value.
|
||||
@trusted
|
||||
void lockFps(int target) {
|
||||
engineState.flags.isFpsLocked = true;
|
||||
ray.SetTargetFPS(target);
|
||||
}
|
||||
|
||||
/// Unlocks the FPS of the game.
|
||||
/// Unlocks the FPS.
|
||||
@trusted
|
||||
void unlockFps() {
|
||||
engineState.flags.isFpsLocked = false;
|
||||
ray.SetTargetFPS(0);
|
||||
}
|
||||
|
||||
/// Returns true if the resolution of the game is locked.
|
||||
/// Returns true if the resolution is locked.
|
||||
bool isResolutionLocked() {
|
||||
return !engineState.viewport.isEmpty;
|
||||
}
|
||||
|
||||
/// Locks the resolution of the game to a specific value.
|
||||
/// Locks the resolution to the given value.
|
||||
@trusted
|
||||
void lockResolution(int width, int height) {
|
||||
if (!engineState.flags.isUpdating) {
|
||||
|
@ -287,7 +286,7 @@ void lockResolution(int width, int height) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Unlocks the resolution of the game.
|
||||
/// Unlocks the resolution.
|
||||
void unlockResolution() {
|
||||
if (!engineState.flags.isUpdating) {
|
||||
engineState.viewport.free();
|
||||
|
@ -311,7 +310,6 @@ bool isCursorHidden() {
|
|||
}
|
||||
|
||||
/// Hides the system cursor.
|
||||
/// This function works only on desktop.
|
||||
@trusted
|
||||
void hideCursor() {
|
||||
engineState.flags.isCursorHidden = true;
|
||||
|
@ -319,7 +317,6 @@ void hideCursor() {
|
|||
}
|
||||
|
||||
/// Shows the system cursor.
|
||||
/// This function works only on desktop.
|
||||
@trusted
|
||||
void showCursor() {
|
||||
engineState.flags.isCursorHidden = false;
|
||||
|
@ -327,14 +324,12 @@ void showCursor() {
|
|||
}
|
||||
|
||||
/// Returns true if the window is in fullscreen mode.
|
||||
/// This function works only on desktop.
|
||||
@trusted
|
||||
bool isFullscreen() {
|
||||
return ray.IsWindowFullscreen();
|
||||
}
|
||||
|
||||
/// Changes the state of the fullscreen mode of the window.
|
||||
/// This function works only on desktop.
|
||||
@trusted
|
||||
void toggleFullscreen() {
|
||||
version(WebAssembly) {
|
||||
|
@ -355,6 +350,7 @@ bool isPixelPerfect() {
|
|||
return engineState.flags.isPixelPerfect;
|
||||
}
|
||||
|
||||
/// Changes the state of the pixel perfect mode of the window.
|
||||
void togglePixelPerfect() {
|
||||
engineState.flags.isPixelPerfect = !engineState.flags.isPixelPerfect;
|
||||
}
|
||||
|
@ -760,7 +756,7 @@ void drawDebugText(IStr text, Vec2 position = Vec2(8.0f), DrawOptions options =
|
|||
drawText(dfltFont, position, text, options);
|
||||
}
|
||||
|
||||
mixin template addGameStart(alias startFunc, int width, int height, IStr title = "Popka") {
|
||||
mixin template callGameStart(alias startFunc, int width, int height, IStr title = "Popka") {
|
||||
version (D_BetterC) {
|
||||
pragma(msg, "Popka is using the C main function.");
|
||||
extern(C)
|
||||
|
|
|
@ -3,13 +3,12 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Email: alexandroskapretsos@gmail.com
|
||||
// Project: https://github.com/Kapendev/popka
|
||||
// Version: v0.0.14
|
||||
// Version: v0.0.15
|
||||
// ---
|
||||
|
||||
// The `popka`` module imports all of its submodules.
|
||||
module popka;
|
||||
|
||||
public import popka.chat;
|
||||
public import popka.dialogue;
|
||||
public import popka.engine;
|
||||
public import popka.tilemap;
|
||||
public import popka.types;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Email: alexandroskapretsos@gmail.com
|
||||
// Project: https://github.com/Kapendev/popka
|
||||
// Version: v0.0.14
|
||||
// Version: v0.0.15
|
||||
// ---
|
||||
|
||||
/// The `ray` module provides access to the raylib library.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Email: alexandroskapretsos@gmail.com
|
||||
// Project: https://github.com/Kapendev/popka
|
||||
// Version: v0.0.14
|
||||
// Version: v0.0.15
|
||||
// ---
|
||||
|
||||
/**********************************************************************************************
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Email: alexandroskapretsos@gmail.com
|
||||
// Project: https://github.com/Kapendev/popka
|
||||
// Version: v0.0.14
|
||||
// Version: v0.0.15
|
||||
// ---
|
||||
|
||||
/**********************************************************************************************
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Email: alexandroskapretsos@gmail.com
|
||||
// Project: https://github.com/Kapendev/popka
|
||||
// Version: v0.0.14
|
||||
// Version: v0.0.15
|
||||
// ---
|
||||
|
||||
/// The `tilemap` module provides a simple and fast tile map.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Email: alexandroskapretsos@gmail.com
|
||||
// Project: https://github.com/Kapendev/popka
|
||||
// Version: v0.0.14
|
||||
// Version: v0.0.15
|
||||
// ---
|
||||
|
||||
/// The `types` module defines all the types used within the `engine` module.
|
||||
|
@ -170,6 +170,15 @@ struct EngineState {
|
|||
Color backgroundColor;
|
||||
LStr tempText;
|
||||
LStr assetsPath;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
void free() {
|
||||
viewport.free();
|
||||
tempText.free();
|
||||
assetsPath.free();
|
||||
this = EngineState();
|
||||
}
|
||||
}
|
||||
|
||||
struct DrawOptions {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue