mirror of
https://github.com/Kapendev/parin.git
synced 2025-04-26 21:19:56 +03:00
Tile map is not part of the engine now.
This commit is contained in:
parent
776070acb4
commit
4850a99236
5 changed files with 246 additions and 229 deletions
|
@ -1,20 +1,17 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// TODO: Needs a lot of work.
|
||||
|
||||
/// The `dialogue` module provides a simple and versatile dialogue system.
|
||||
module popka.dialogue;
|
||||
/// The `chat` module provides a simple and versatile dialogue system.
|
||||
module popka.chat;
|
||||
|
||||
import popka.engine;
|
||||
|
||||
public import joka;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
enum dialogueUnitKindChars = ".#*@>|^!+-$";
|
||||
enum ChatUnitKindChars = ".#*@>|^!+-$";
|
||||
|
||||
enum DialogueUnitKind {
|
||||
enum ChatUnitKind {
|
||||
pause = '.',
|
||||
comment = '#',
|
||||
point = '*',
|
||||
|
@ -28,27 +25,24 @@ enum DialogueUnitKind {
|
|||
command = '$',
|
||||
}
|
||||
|
||||
struct DialogueUnit {
|
||||
List!char text;
|
||||
DialogueUnitKind kind;
|
||||
struct ChatUnit {
|
||||
LStr text;
|
||||
ChatUnitKind kind;
|
||||
}
|
||||
|
||||
struct DialogueVariable {
|
||||
List!char name;
|
||||
struct ChatValue {
|
||||
LStr name;
|
||||
long value;
|
||||
}
|
||||
|
||||
alias DialogueCommandRunner = void function(const(char)[][] args);
|
||||
alias ChatCommandRunner = void function(IStr[] args);
|
||||
|
||||
struct Dialogue {
|
||||
List!DialogueUnit units;
|
||||
List!DialogueVariable variables;
|
||||
List!(const(char)[]) menu;
|
||||
List!(const(char)[]) command;
|
||||
size_t unitIndex;
|
||||
size_t pointCount;
|
||||
const(char)[] text;
|
||||
const(char)[] actor;
|
||||
struct Chat {
|
||||
List!ChatUnit units;
|
||||
List!ChatValue values;
|
||||
IStr text;
|
||||
IStr actor;
|
||||
Sz unitIndex;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
|
@ -56,43 +50,84 @@ struct Dialogue {
|
|||
return units.length == 0;
|
||||
}
|
||||
|
||||
bool isKind(ChatUnitKind kind) {
|
||||
return unitIndex < units.length && units[unitIndex].kind == kind;
|
||||
}
|
||||
|
||||
bool hasChoices() {
|
||||
return menu.length != 0;
|
||||
return isKind(ChatUnitKind.menu);
|
||||
}
|
||||
|
||||
bool hasCommand() {
|
||||
return command.length != 0;
|
||||
}
|
||||
|
||||
bool hasText() {
|
||||
return unitIndex < units.length && units[unitIndex].kind != DialogueUnitKind.pause;
|
||||
bool hasArgs() {
|
||||
return isKind(ChatUnitKind.command);
|
||||
}
|
||||
|
||||
bool hasEnded() {
|
||||
return !hasText;
|
||||
return isKind(ChatUnitKind.pause);
|
||||
}
|
||||
|
||||
const(char)[][] choices() {
|
||||
return menu.items;
|
||||
auto choices() {
|
||||
struct Range {
|
||||
IStr menu;
|
||||
|
||||
bool empty() {
|
||||
return menu.length == 0;
|
||||
}
|
||||
|
||||
const(char)[][] args() {
|
||||
return command.items;
|
||||
IStr front() {
|
||||
auto temp = menu;
|
||||
return temp.skipValue(ChatUnitKind.menu).trim();
|
||||
}
|
||||
|
||||
void popFront() {
|
||||
menu.skipValue(ChatUnitKind.menu);
|
||||
}
|
||||
}
|
||||
|
||||
return hasChoices ? Range(units[unitIndex].text.items) : Range("");
|
||||
}
|
||||
|
||||
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;
|
||||
length += 1;
|
||||
}
|
||||
return buffer[0 .. length];
|
||||
}
|
||||
|
||||
void reset() {
|
||||
menu.clear();
|
||||
command.clear();
|
||||
unitIndex = 0;
|
||||
text = "";
|
||||
actor = "";
|
||||
unitIndex = 0;
|
||||
}
|
||||
|
||||
void jump(const(char)[] point) {
|
||||
// TODO: Make it faster.
|
||||
void jump(IStr point) {
|
||||
if (point.length == 0) {
|
||||
foreach (i; unitIndex + 1 .. units.length) {
|
||||
auto unit = units[i];
|
||||
if (unit.kind == DialogueUnitKind.point) {
|
||||
if (unit.kind == ChatUnitKind.point) {
|
||||
unitIndex = i;
|
||||
break;
|
||||
}
|
||||
|
@ -100,7 +135,7 @@ struct Dialogue {
|
|||
} else {
|
||||
foreach (i; 0 .. units.length) {
|
||||
auto unit = units[i];
|
||||
if (unit.kind == DialogueUnitKind.point && unit.text.items == point) {
|
||||
if (unit.kind == ChatUnitKind.point && unit.text.items == point) {
|
||||
unitIndex = i;
|
||||
break;
|
||||
}
|
||||
|
@ -108,10 +143,11 @@ struct Dialogue {
|
|||
}
|
||||
}
|
||||
|
||||
void jump(size_t i) {
|
||||
// TODO: Make it faster.
|
||||
void jump(Sz i) {
|
||||
auto currPoint = 0;
|
||||
foreach (j, unit; units.items) {
|
||||
if (unit.kind == DialogueUnitKind.point) {
|
||||
if (unit.kind == ChatUnitKind.point) {
|
||||
if (currPoint == i) {
|
||||
unitIndex = j;
|
||||
break;
|
||||
|
@ -121,21 +157,19 @@ struct Dialogue {
|
|||
}
|
||||
}
|
||||
|
||||
void skip(size_t count) {
|
||||
void skip(Sz count) {
|
||||
foreach (i; 0 .. count) {
|
||||
jump("");
|
||||
}
|
||||
}
|
||||
|
||||
void select(size_t i) {
|
||||
menu.clear();
|
||||
void pick(Sz i) {
|
||||
skip(i + 1);
|
||||
update();
|
||||
}
|
||||
|
||||
void run(DialogueCommandRunner runner) {
|
||||
void run(ChatCommandRunner runner) {
|
||||
runner(args);
|
||||
command.clear();
|
||||
update();
|
||||
}
|
||||
|
||||
|
@ -145,36 +179,24 @@ struct Dialogue {
|
|||
unitIndex += 1;
|
||||
text = units[unitIndex].text.items;
|
||||
final switch (units[unitIndex].kind) {
|
||||
case DialogueUnitKind.pause, DialogueUnitKind.line: {
|
||||
case ChatUnitKind.line, ChatUnitKind.menu, ChatUnitKind.command, ChatUnitKind.pause: {
|
||||
break;
|
||||
}
|
||||
case DialogueUnitKind.comment, DialogueUnitKind.point: {
|
||||
case ChatUnitKind.comment, ChatUnitKind.point: {
|
||||
update();
|
||||
break;
|
||||
}
|
||||
case DialogueUnitKind.actor: {
|
||||
case ChatUnitKind.actor: {
|
||||
actor = text;
|
||||
update();
|
||||
break;
|
||||
}
|
||||
case DialogueUnitKind.jump: {
|
||||
case ChatUnitKind.jump: {
|
||||
jump(text);
|
||||
update();
|
||||
break;
|
||||
}
|
||||
case DialogueUnitKind.menu: {
|
||||
if (text.length == 0) {
|
||||
assert(0, "TODO: An empty menu is an error for now.");
|
||||
}
|
||||
menu.clear();
|
||||
auto view = text;
|
||||
while (view.length != 0) {
|
||||
auto option = trim(skipValue(view, DialogueUnitKind.menu));
|
||||
menu.append(option);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DialogueUnitKind.variable: {
|
||||
case ChatUnitKind.variable: {
|
||||
auto variableIndex = -1;
|
||||
auto view = text;
|
||||
auto name = trim(skipValue(view, '='));
|
||||
|
@ -183,7 +205,7 @@ struct Dialogue {
|
|||
assert(0, "TODO: An variable without a name is an error for now.");
|
||||
}
|
||||
// Find if variable exists.
|
||||
foreach (i, variable; variables.items) {
|
||||
foreach (i, variable; values.items) {
|
||||
if (variable.name.items == name) {
|
||||
variableIndex = cast(int) i;
|
||||
break;
|
||||
|
@ -191,10 +213,10 @@ struct Dialogue {
|
|||
}
|
||||
// Create variable if it does not exist.
|
||||
if (variableIndex < 0) {
|
||||
auto variable = DialogueVariable();
|
||||
auto variable = ChatValue();
|
||||
variable.name.append(name);
|
||||
variables.append(variable);
|
||||
variableIndex = cast(int) variables.length - 1;
|
||||
values.append(variable);
|
||||
variableIndex = cast(int) values.length - 1;
|
||||
}
|
||||
// Set variable value.
|
||||
if (value.length != 0) {
|
||||
|
@ -203,7 +225,7 @@ struct Dialogue {
|
|||
auto valueVariableIndex = -1;
|
||||
auto valueName = value;
|
||||
// Find if variable exists.
|
||||
foreach (i, variable; variables.items) {
|
||||
foreach (i, variable; values.items) {
|
||||
if (variable.name.items == valueName) {
|
||||
valueVariableIndex = cast(int) i;
|
||||
break;
|
||||
|
@ -212,20 +234,20 @@ struct Dialogue {
|
|||
if (valueVariableIndex < 0) {
|
||||
assert(0, "TODO: A variable that doesn't exist it an error for now.");
|
||||
} else {
|
||||
variables[variableIndex].value = variables[valueVariableIndex].value;
|
||||
values[variableIndex].value = values[valueVariableIndex].value;
|
||||
}
|
||||
} else {
|
||||
variables[variableIndex].value = conv.value;
|
||||
values[variableIndex].value = conv.value;
|
||||
}
|
||||
}
|
||||
update();
|
||||
break;
|
||||
}
|
||||
case DialogueUnitKind.plus, DialogueUnitKind.minus: {
|
||||
case ChatUnitKind.plus, ChatUnitKind.minus: {
|
||||
auto variableIndex = -1;
|
||||
auto name = text;
|
||||
// Find if variable exists.
|
||||
foreach (i, variable; variables.items) {
|
||||
foreach (i, variable; values.items) {
|
||||
if (variable.name.items == name) {
|
||||
variableIndex = cast(int) i;
|
||||
break;
|
||||
|
@ -235,37 +257,25 @@ struct Dialogue {
|
|||
if (variableIndex < 0) {
|
||||
assert(0, "TODO: A variable that doesn't exist it an error for now.");
|
||||
}
|
||||
if (units[unitIndex].kind == DialogueUnitKind.plus) {
|
||||
variables[variableIndex].value += 1;
|
||||
if (units[unitIndex].kind == ChatUnitKind.plus) {
|
||||
values[variableIndex].value += 1;
|
||||
} else {
|
||||
variables[variableIndex].value -= 1;
|
||||
values[variableIndex].value -= 1;
|
||||
}
|
||||
update();
|
||||
break;
|
||||
}
|
||||
case DialogueUnitKind.command: {
|
||||
if (text.length == 0) {
|
||||
assert(0, "TODO: An empty command is an error for now.");
|
||||
}
|
||||
command.clear();
|
||||
auto view = text;
|
||||
while (view.length != 0) {
|
||||
auto arg = trim(skipValue(view, ' '));
|
||||
command.append(arg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Fault parse(const(char)[] script) {
|
||||
Fault parse(IStr script) {
|
||||
clear();
|
||||
if (script.length == 0) {
|
||||
return Fault.invalid;
|
||||
}
|
||||
|
||||
units.append(DialogueUnit(List!char(), DialogueUnitKind.pause));
|
||||
units.append(ChatUnit(LStr(), ChatUnitKind.pause));
|
||||
auto isFirstLine = true;
|
||||
auto view = script;
|
||||
while (view.length != 0) {
|
||||
|
@ -277,23 +287,20 @@ struct Dialogue {
|
|||
auto kind = line[0];
|
||||
if (isFirstLine) {
|
||||
isFirstLine = false;
|
||||
if (kind == DialogueUnitKind.pause) {
|
||||
if (kind == ChatUnitKind.pause) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (isValidDialogueUnitKind(kind)) {
|
||||
auto realKind = cast(DialogueUnitKind) kind;
|
||||
units.append(DialogueUnit(List!char(text), realKind));
|
||||
if (realKind == DialogueUnitKind.point) {
|
||||
pointCount += 1;
|
||||
}
|
||||
if (isValidChatUnitKind(kind)) {
|
||||
auto realKind = cast(ChatUnitKind) kind;
|
||||
units.append(ChatUnit(LStr(text), realKind));
|
||||
} else {
|
||||
clear();
|
||||
return Fault.invalid;
|
||||
}
|
||||
}
|
||||
if (units.items[$ - 1].kind != DialogueUnitKind.pause) {
|
||||
units.append(DialogueUnit(List!char(), DialogueUnitKind.pause));
|
||||
if (units.items[$ - 1].kind != ChatUnitKind.pause) {
|
||||
units.append(ChatUnit(LStr(), ChatUnitKind.pause));
|
||||
}
|
||||
return Fault.none;
|
||||
}
|
||||
|
@ -303,14 +310,11 @@ struct Dialogue {
|
|||
unit.text.free();
|
||||
}
|
||||
units.clear();
|
||||
foreach (ref variable; variables) {
|
||||
foreach (ref variable; values) {
|
||||
variable.name.free();
|
||||
}
|
||||
variables.clear();
|
||||
menu.clear();
|
||||
command.clear();
|
||||
values.clear();
|
||||
reset();
|
||||
pointCount = 0;
|
||||
}
|
||||
|
||||
void free() {
|
||||
|
@ -318,19 +322,16 @@ struct Dialogue {
|
|||
unit.text.free();
|
||||
}
|
||||
units.free();
|
||||
foreach (ref variable; variables) {
|
||||
foreach (ref variable; values) {
|
||||
variable.name.free();
|
||||
}
|
||||
variables.free();
|
||||
menu.free();
|
||||
command.free();
|
||||
values.free();
|
||||
reset();
|
||||
pointCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool isValidDialogueUnitKind(char c) {
|
||||
foreach (kind; dialogueUnitKindChars) {
|
||||
bool isValidChatUnitKind(char c) {
|
||||
foreach (kind; ChatUnitKindChars) {
|
||||
if (c == kind) {
|
||||
return true;
|
||||
}
|
||||
|
@ -338,16 +339,19 @@ bool isValidDialogueUnitKind(char c) {
|
|||
return false;
|
||||
}
|
||||
|
||||
Result!Dialogue loadDialogue(IStr path) {
|
||||
auto temp = loadTempText(path);
|
||||
if (temp.isNone) {
|
||||
return Result!Dialogue(temp.fault);
|
||||
}
|
||||
|
||||
auto value = Dialogue();
|
||||
auto fault = value.parse(temp.unwrap());
|
||||
Result!Chat toChat(IStr script) {
|
||||
auto value = Chat();
|
||||
auto fault = value.parse(script);
|
||||
if (fault) {
|
||||
value.free();
|
||||
}
|
||||
return Result!Dialogue(value, fault);
|
||||
return Result!Chat(value, fault);
|
||||
}
|
||||
|
||||
Result!Chat loadChat(IStr path) {
|
||||
auto temp = loadTempText(path);
|
||||
if (temp.isNone) {
|
||||
return Result!Chat(temp.fault);
|
||||
}
|
||||
return toChat(temp.unwrap());
|
||||
}
|
|
@ -1,13 +1,10 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// TODO: Needs docs!
|
||||
|
||||
/// The `engine` module functions as a lightweight 2D game engine.
|
||||
module popka.engine;
|
||||
|
||||
import ray = popka.ray;
|
||||
|
||||
public import joka;
|
||||
public import popka.types;
|
||||
|
||||
|
@ -88,15 +85,6 @@ Result!IStr loadTempText(IStr path) {
|
|||
return Result!IStr(engineState.tempText.items, fault);
|
||||
}
|
||||
|
||||
Result!TileMap loadTileMap(IStr path) {
|
||||
auto value = TileMap();
|
||||
auto fault = value.parse(loadTempText(path).unwrapOr());
|
||||
if (fault) {
|
||||
value.free();
|
||||
}
|
||||
return Result!TileMap(value, fault);
|
||||
}
|
||||
|
||||
/// Loads an image file from the assets folder.
|
||||
/// Can handle both forward slashes and backslashes in file paths, ensuring compatibility across operating systems.
|
||||
@trusted
|
||||
|
@ -662,38 +650,6 @@ void drawTile(Texture texture, Vec2 position, int tileID, Vec2 tileSize, DrawOpt
|
|||
drawTexture(texture, position, area, options);
|
||||
}
|
||||
|
||||
void drawTileMap(Texture texture, Vec2 position, TileMap tileMap, Camera camera, DrawOptions options = DrawOptions()) {
|
||||
enum extraTileCount = 4;
|
||||
|
||||
auto cameraArea = Rect(camera.position, resolution).area(camera.hook);
|
||||
auto topLeft = cameraArea.point(Hook.topLeft);
|
||||
auto bottomRight = cameraArea.point(Hook.bottomRight);
|
||||
auto col1 = 0;
|
||||
auto col2 = 0;
|
||||
auto row1 = 0;
|
||||
auto row2 = 0;
|
||||
|
||||
if (camera.isAttached) {
|
||||
col1 = cast(int) floor(clamp((topLeft.x - position.x) / tileMap.tileSize.x - extraTileCount, 0, tileMap.colCount));
|
||||
col2 = cast(int) floor(clamp((bottomRight.x - position.x) / tileMap.tileSize.x + extraTileCount, 0, tileMap.colCount));
|
||||
row1 = cast(int) floor(clamp((topLeft.y - position.y) / tileMap.tileSize.y - extraTileCount, 0, tileMap.rowCount));
|
||||
row2 = cast(int) floor(clamp((bottomRight.y - position.y) / tileMap.tileSize.y + extraTileCount, 0, tileMap.rowCount));
|
||||
} else {
|
||||
col1 = 0;
|
||||
col2 = cast(int) tileMap.colCount;
|
||||
row1 = 0;
|
||||
row2 = cast(int) tileMap.rowCount;
|
||||
}
|
||||
foreach (row; row1 .. row2) {
|
||||
foreach (col; col1 .. col2) {
|
||||
if (tileMap[row, col] == -1) {
|
||||
continue;
|
||||
}
|
||||
drawTile(texture, position + Vec2(col, row) * tileMap.tileSize * options.scale, tileMap[row, col], tileMap.tileSize, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@trusted
|
||||
void drawRune(Font font, Vec2 position, dchar rune, DrawOptions options = DrawOptions()) {
|
||||
if (font.isEmpty) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
module popka;
|
||||
|
||||
public import popka.dialogue;
|
||||
public import popka.chat;
|
||||
public import popka.engine;
|
||||
public import popka.tilemap;
|
||||
public import popka.types;
|
||||
|
|
119
source/popka/tilemap.d
Normal file
119
source/popka/tilemap.d
Normal file
|
@ -0,0 +1,119 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/// The `tilemap` module provides a simple and fast tile map.
|
||||
module popka.tilemap;
|
||||
|
||||
import popka.engine;
|
||||
public import joka;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
struct TileMap {
|
||||
Grid!short data;
|
||||
int tileWidth;
|
||||
int tileHeight;
|
||||
|
||||
alias data this;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
/// Returns true if the tile map has not been loaded.
|
||||
bool isEmpty() {
|
||||
return data.length == 0;
|
||||
}
|
||||
|
||||
/// Returns the tile size of the tile map.
|
||||
Vec2 tileSize() {
|
||||
return Vec2(tileWidth, tileHeight);
|
||||
}
|
||||
|
||||
/// Returns the size of the tile map.
|
||||
Vec2 size() {
|
||||
return tileSize * Vec2(colCount, rowCount);
|
||||
}
|
||||
|
||||
Fault parse(IStr csv) {
|
||||
data.clear();
|
||||
if (csv.length == 0) {
|
||||
return Fault.invalid;
|
||||
}
|
||||
|
||||
auto view = csv;
|
||||
auto newRowCount = 0;
|
||||
auto newColCount = 0;
|
||||
while (view.length != 0) {
|
||||
auto line = view.skipLine();
|
||||
newRowCount += 1;
|
||||
newColCount = 0;
|
||||
while (line.length != 0) {
|
||||
auto value = line.skipValue(',');
|
||||
newColCount += 1;
|
||||
}
|
||||
}
|
||||
resize(newRowCount, newColCount);
|
||||
|
||||
view = csv;
|
||||
foreach (row; 0 .. newRowCount) {
|
||||
auto line = view.skipLine();
|
||||
foreach (col; 0 .. newColCount) {
|
||||
auto value = line.skipValue(',').toSigned();
|
||||
if (value.isNone) {
|
||||
data.clear();
|
||||
return Fault.invalid;
|
||||
}
|
||||
data[row, col] = cast(short) value.unwrap();
|
||||
}
|
||||
}
|
||||
return Fault.none;
|
||||
}
|
||||
}
|
||||
|
||||
Result!TileMap toTileMap(IStr csv) {
|
||||
auto value = TileMap();
|
||||
auto fault = value.parse(csv);
|
||||
if (fault) {
|
||||
value.free();
|
||||
}
|
||||
return Result!TileMap(value, fault);
|
||||
}
|
||||
|
||||
Result!TileMap loadTileMap(IStr path) {
|
||||
auto temp = loadTempText(path);
|
||||
if (temp.isNone) {
|
||||
return Result!TileMap(temp.fault);
|
||||
}
|
||||
return toTileMap(temp.unwrap());
|
||||
}
|
||||
|
||||
void drawTileMap(Texture texture, Vec2 position, TileMap tileMap, Camera camera, DrawOptions options = DrawOptions()) {
|
||||
enum extraTileCount = 1;
|
||||
|
||||
auto cameraArea = Rect(camera.position, resolution).area(camera.hook);
|
||||
auto topLeft = cameraArea.point(Hook.topLeft);
|
||||
auto bottomRight = cameraArea.point(Hook.bottomRight);
|
||||
auto col1 = 0;
|
||||
auto col2 = 0;
|
||||
auto row1 = 0;
|
||||
auto row2 = 0;
|
||||
|
||||
if (camera.isAttached) {
|
||||
col1 = cast(int) floor(clamp((topLeft.x - position.x) / tileMap.tileSize.x - extraTileCount, 0, tileMap.colCount));
|
||||
col2 = cast(int) floor(clamp((bottomRight.x - position.x) / tileMap.tileSize.x + extraTileCount, 0, tileMap.colCount));
|
||||
row1 = cast(int) floor(clamp((topLeft.y - position.y) / tileMap.tileSize.y - extraTileCount, 0, tileMap.rowCount));
|
||||
row2 = cast(int) floor(clamp((bottomRight.y - position.y) / tileMap.tileSize.y + extraTileCount, 0, tileMap.rowCount));
|
||||
} else {
|
||||
col1 = 0;
|
||||
col2 = cast(int) tileMap.colCount;
|
||||
row1 = 0;
|
||||
row2 = cast(int) tileMap.rowCount;
|
||||
}
|
||||
foreach (row; row1 .. row2) {
|
||||
foreach (col; col1 .. col2) {
|
||||
if (tileMap[row, col] == -1) {
|
||||
continue;
|
||||
}
|
||||
drawTile(texture, position + Vec2(col, row) * tileMap.tileSize * options.scale, tileMap[row, col], tileMap.tileSize, options);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,10 @@
|
|||
// Copyright 2024 Alexandros F. G. Kapretsos
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// TODO: Needs docs!
|
||||
// TODO: Move tile map to it's own module like dialogue!
|
||||
|
||||
/// The `types` module defines all the types used within the `engine` module.
|
||||
module popka.types;
|
||||
|
||||
import ray = popka.ray;
|
||||
|
||||
public import joka;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
@ -151,6 +147,7 @@ struct EngineViewport {
|
|||
int targetHeight;
|
||||
bool isLockResolutionQueued;
|
||||
bool isUnlockResolutionQueued;
|
||||
|
||||
alias data this;
|
||||
}
|
||||
|
||||
|
@ -209,66 +206,6 @@ struct Camera {
|
|||
}
|
||||
}
|
||||
|
||||
struct TileMap {
|
||||
Grid!short data;
|
||||
int tileWidth;
|
||||
int tileHeight;
|
||||
alias data this;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
/// Returns true if the tile map has not been loaded.
|
||||
bool isEmpty() {
|
||||
return data.length == 0;
|
||||
}
|
||||
|
||||
/// Returns the tile size of the tile map.
|
||||
Vec2 tileSize() {
|
||||
return Vec2(tileWidth, tileHeight);
|
||||
}
|
||||
|
||||
/// Returns the size of the tile map.
|
||||
Vec2 size() {
|
||||
return tileSize * Vec2(colCount, rowCount);
|
||||
}
|
||||
|
||||
Fault parse(IStr csv) {
|
||||
data.clear();
|
||||
if (csv.length == 0) {
|
||||
return Fault.invalid;
|
||||
}
|
||||
|
||||
auto view = csv;
|
||||
auto newRowCount = 0;
|
||||
auto newColCount = 0;
|
||||
|
||||
while (view.length != 0) {
|
||||
auto line = view.skipLine();
|
||||
newRowCount += 1;
|
||||
newColCount = 0;
|
||||
while (line.length != 0) {
|
||||
auto value = line.skipValue(',');
|
||||
newColCount += 1;
|
||||
}
|
||||
}
|
||||
resize(newRowCount, newColCount);
|
||||
|
||||
view = csv;
|
||||
foreach (row; 0 .. newRowCount) {
|
||||
auto line = view.skipLine();
|
||||
foreach (col; 0 .. newColCount) {
|
||||
auto value = line.skipValue(',').toSigned();
|
||||
if (value.isNone) {
|
||||
data.clear();
|
||||
return Fault.invalid;
|
||||
}
|
||||
data[row, col] = cast(short) value.unwrap();
|
||||
}
|
||||
}
|
||||
return Fault.none;
|
||||
}
|
||||
}
|
||||
|
||||
struct Texture {
|
||||
ray.Texture2D data;
|
||||
Filter filter;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue