First commit of what I have so far
This commit is contained in:
commit
7cda3bcc46
|
@ -0,0 +1,3 @@
|
|||
assets.tar
|
||||
zig-out
|
||||
.zig-cache
|
|
@ -0,0 +1,70 @@
|
|||
Licence Libre du Québec – Permissive (LiLiQ-P)
|
||||
|
||||
Version 1.1
|
||||
|
||||
1. Préambule
|
||||
Cette licence s'applique à tout logiciel distribué dont le titulaire du droit d'auteur précise qu'il est sujet aux termes de la Licence Libre du Québec – Permissive (LiLiQ-P) (ci-après appelée la « licence »).
|
||||
|
||||
2. Définitions
|
||||
Dans la présente licence, à moins que le contexte n'indique un sens différent, on entend par:
|
||||
|
||||
« concédant » : le titulaire du droit d'auteur sur le logiciel, ou toute personne dûment autorisée par ce dernier à accorder la présente licence;
|
||||
« contributeur » : le titulaire du droit d'auteur ou toute personne autorisée par ce dernier à soumettre au concédant une contribution. Un contributeur dont sa contribution est incorporée au logiciel est considéré comme un concédant en regard de sa contribution;
|
||||
« contribution » : tout logiciel original, ou partie de logiciel original soumis et destiné à être incorporé dans le logiciel;
|
||||
« distribution » : le fait de délivrer une copie du logiciel;
|
||||
« licencié » : toute personne qui possède une copie du logiciel et qui exerce les droits concédés par la licence;
|
||||
« logiciel » : une œuvre protégée par le droit d'auteur, telle qu'un programme d'ordinateur et sa documentation, pour laquelle le titulaire du droit d'auteur a précisé qu'elle est sujette aux termes de la présente licence;
|
||||
« logiciel dérivé » : tout logiciel original réalisé par un licencié, autre que le logiciel ou un logiciel modifié, qui produit ou reproduit la totalité ou une partie importante du logiciel;
|
||||
« logiciel modifié » : toute modification par un licencié de l'un des fichiers source du logiciel ou encore tout nouveau fichier source qui incorpore le logiciel ou une partie importante de ce dernier.
|
||||
|
||||
3. Licence de droit d'auteur
|
||||
Sous réserve des termes de la licence, le concédant accorde au licencié une licence non exclusive et libre de redevances lui permettant d’exercer les droits suivants sur le logiciel :
|
||||
|
||||
1 Produire ou reproduire la totalité ou une partie importante;
|
||||
2 Exécuter ou représenter la totalité ou une partie importante en public;
|
||||
3 Publier la totalité ou une partie importante;
|
||||
4 Sous-licencier sous une autre licence libre, approuvée ou certifiée par la Free Software Foundation ou l'Open Source Initiative.
|
||||
|
||||
Cette licence est accordée sans limite territoriale et sans limite de temps.
|
||||
|
||||
L'exercice complet de ces droits est sujet à la distribution par le concédant du code source du logiciel, lequel doit être sous une forme permettant d'y apporter des modifications. Le concédant peut aussi distribuer le logiciel accompagné d'une offre de distribuer le code source du logiciel, sans frais supplémentaires, autres que ceux raisonnables afin de permettre la livraison du code source. Cette offre doit être valide pendant une durée raisonnable.
|
||||
|
||||
4. Distribution
|
||||
Le licencié peut distribuer des copies du logiciel, d'un logiciel modifié ou dérivé, sous réserve de respecter les conditions suivantes :
|
||||
|
||||
1 Le logiciel doit être accompagné d'un exemplaire de cette licence;
|
||||
2 Si le logiciel a été modifié, le licencié doit en faire la mention, de préférence dans chacun des fichiers modifiés dont la nature permet une telle mention;
|
||||
3 Les étiquettes ou mentions faisant état des droits d'auteur, des marques de commerce, des garanties ou de la paternité concernant le logiciel ne doivent pas être modifiées ou supprimées, à moins que ces étiquettes ou mentions ne soient inapplicables à un logiciel modifié ou dérivé donné.
|
||||
|
||||
5. Contributions
|
||||
Sous réserve d'une entente distincte, toute contribution soumise par un contributeur au concédant pour inclusion dans le logiciel sera soumise aux termes de cette licence.
|
||||
|
||||
6. Marques de commerce
|
||||
La licence n'accorde aucune permission particulière qui permettrait d'utiliser les marques de commerce du concédant, autre que celle requise permettant d'identifier la provenance du logiciel.
|
||||
|
||||
7. Garanties
|
||||
Sauf mention contraire, le concédant distribue le logiciel sans aucune garantie, aux risques et périls de l'acquéreur de la copie du logiciel, et ce, sans assurer que le logiciel puisse répondre à un besoin particulier ou puisse donner un résultat quelconque.
|
||||
|
||||
Sans lier le concédant d'une quelconque manière, rien n'empêche un licencié d'offrir ou d'exclure des garanties ou du support.
|
||||
|
||||
8. Responsabilité
|
||||
Le licencié est responsable de tout préjudice résultant de l'exercice des droits accordés par la licence.
|
||||
|
||||
Le concédant ne saurait être tenu responsable de dommages subis par le licencié ou par des tiers, pour quelque cause que ce soit en lien avec la licence et les droits qui y sont accordés.
|
||||
|
||||
9. Résiliation
|
||||
La présente licence est automatiquement résiliée dès que les droits qui y sont accordés ne sont pas exercés conformément aux termes qui y sont stipulés.
|
||||
|
||||
Toutefois, si le défaut est corrigé dans un délai de 30 jours de sa prise de connaissance par la personne en défaut, et qu'il s'agit du premier défaut, la licence est accordée de nouveau.
|
||||
|
||||
Pour tout défaut subséquent, le consentement exprès du concédant est nécessaire afin que la licence soit accordée de nouveau.
|
||||
|
||||
10. Version de la licence
|
||||
Le Centre de services partagés du Québec, ses ayants cause ou toute personne qu'il désigne, peuvent diffuser des versions révisées ou modifiées de cette licence. Chaque version recevra un numéro unique. Si un logiciel est déjà soumis aux termes d'une version spécifique, c'est seulement cette version qui liera les parties à la licence.
|
||||
|
||||
Le concédant peut aussi choisir de concéder la licence sous la version actuelle ou toute version ultérieure, auquel cas le licencié peut choisir sous quelle version la licence lui est accordée.
|
||||
|
||||
11. Divers
|
||||
Dans la mesure où le concédant est un ministère, un organisme public ou une personne morale de droit public, créés en vertu d'une loi de l'Assemblée nationale du Québec, la licence est régie par le droit applicable au Québec et en cas de contestation, les tribunaux du Québec seront seuls compétents.
|
||||
|
||||
La présente licence peut être distribuée sans conditions particulières. Toutefois, une version modifiée doit être distribuée sous un nom différent. Toute référence au Centre de services partagés du Québec, et, le cas échéant, ses ayant cause, doit être retirée, autre que celle permettant d'identifier la provenance de la licence.
|
|
@ -0,0 +1,7 @@
|
|||
# The Walrus Game
|
||||
|
||||
an unfinished game by Yuki, for the CodeWalrus 10th Anniversary Game Jam
|
||||
|
||||
compile with `zig build`
|
||||
|
||||
have fun
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="20" height="15" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||
<tileset firstgid="1" name="sprites" tilewidth="32" tileheight="32" tilecount="40" columns="8">
|
||||
<image source="sprites.png" width="256" height="160"/>
|
||||
</tileset>
|
||||
<tileset firstgid="41" name="sprites" tilewidth="32" tileheight="32" tilecount="40" columns="8">
|
||||
<image source="sprites.png" width="256" height="160"/>
|
||||
<tile id="0" type="spawne"/>
|
||||
<tile id="1" type="spawns"/>
|
||||
<tile id="2" type="spawnw"/>
|
||||
<tile id="3" type="spawnn"/>
|
||||
<tile id="4" type="exitc"/>
|
||||
<tile id="5" type="exito"/>
|
||||
<tile id="6" type="dire"/>
|
||||
<tile id="7" type="dirs"/>
|
||||
<tile id="8" type="dirw"/>
|
||||
<tile id="9" type="dirn"/>
|
||||
<tile id="10" type="add"/>
|
||||
<tile id="11" type="sub"/>
|
||||
<tile id="12" type="mul"/>
|
||||
<tile id="13" type="div"/>
|
||||
<tile id="14" type="rem"/>
|
||||
<tile id="15" type="target"/>
|
||||
<tile id="16" type="ok"/>
|
||||
<tile id="17" type="stop"/>
|
||||
<tile id="18" type="empty"/>
|
||||
<tile id="19" type="0"/>
|
||||
<tile id="20" type="1"/>
|
||||
<tile id="21" type="2"/>
|
||||
<tile id="22" type="3"/>
|
||||
<tile id="23" type="4"/>
|
||||
<tile id="24" type="5"/>
|
||||
<tile id="25" type="6"/>
|
||||
<tile id="26" type="7"/>
|
||||
<tile id="27" type="8"/>
|
||||
<tile id="28" type="9"/>
|
||||
</tileset>
|
||||
<layer id="1" name="level" class="level,walrus" width="20" height="15">
|
||||
<data encoding="base64" compression="zstd">
|
||||
KLUv/WCwAyUBAKgAAwAAABkAAAAjAAAAM0QiAAAABgAEAH9wtUB2WSGfHdWxAQ==
|
||||
</data>
|
||||
</layer>
|
||||
</map>
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="15" height="10" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||
<properties>
|
||||
<property name="name" value="Level 1"/>
|
||||
</properties>
|
||||
<tileset firstgid="1" name="sprites" tilewidth="32" tileheight="32" tilecount="40" columns="8">
|
||||
<image source="sprites.png" width="256" height="160"/>
|
||||
</tileset>
|
||||
<layer id="1" name="lvl" class="level,walrus,player" width="15" height="10">
|
||||
<data encoding="base64" compression="zstd">
|
||||
KLUv/WBYAa0AACgAAx0GAAUAO0ARt2IXb8VlfCgnBA==
|
||||
</data>
|
||||
</layer>
|
||||
</map>
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"automappingRulesFile": "",
|
||||
"commands": [
|
||||
],
|
||||
"compatibilityVersion": 1100,
|
||||
"extensionsPath": "extensions",
|
||||
"folders": [
|
||||
"."
|
||||
],
|
||||
"properties": [
|
||||
],
|
||||
"propertyTypes": [
|
||||
]
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
{
|
||||
"Map/SizeTest": {
|
||||
"height": 4300,
|
||||
"width": 2
|
||||
},
|
||||
"activeFile": "intro.tmx",
|
||||
"expandedProjectPaths": [
|
||||
"."
|
||||
],
|
||||
"fileStates": {
|
||||
"": {
|
||||
"scaleInDock": 1
|
||||
},
|
||||
"intro.tmx": {
|
||||
"scale": 1,
|
||||
"selectedLayer": 0,
|
||||
"viewCenter": {
|
||||
"x": 360.5,
|
||||
"y": 258
|
||||
}
|
||||
},
|
||||
"intro.tmx#sprites": {
|
||||
"scaleInDock": 1
|
||||
},
|
||||
"intro2.tmx": {
|
||||
"scale": 1.0667708333333332,
|
||||
"selectedLayer": 0,
|
||||
"viewCenter": {
|
||||
"x": 479.95312957718977,
|
||||
"y": 319.65628356605805
|
||||
}
|
||||
},
|
||||
"intro2.tmx#sprites": {
|
||||
"scaleInDock": 1
|
||||
},
|
||||
"lvl001.tmx": {
|
||||
"scale": 1.9771874999999997,
|
||||
"selectedLayer": 0,
|
||||
"viewCenter": {
|
||||
"x": 239.9873557768295,
|
||||
"y": 170.94989726568676
|
||||
}
|
||||
},
|
||||
"lvl001.tmx#sprites": {
|
||||
"scaleInDock": 1
|
||||
},
|
||||
"sprites.tsx": {
|
||||
"scaleInDock": 1,
|
||||
"scaleInEditor": 1
|
||||
},
|
||||
"test.tmx": {
|
||||
"scale": 0.9885937499999998,
|
||||
"selectedLayer": 0,
|
||||
"viewCenter": {
|
||||
"x": 479.974711553659,
|
||||
"y": 319.64596175122495
|
||||
}
|
||||
},
|
||||
"test.tmx#sprites": {
|
||||
"scaleInDock": 1
|
||||
},
|
||||
"wrap.tmx": {
|
||||
"scale": 1.9771874999999997,
|
||||
"selectedLayer": 0,
|
||||
"viewCenter": {
|
||||
"x": 239.9873557768295,
|
||||
"y": 148.94894894894895
|
||||
}
|
||||
},
|
||||
"wrap.tmx#sprites": {
|
||||
"scaleInDock": 1
|
||||
},
|
||||
"write.tmx": {
|
||||
"scale": 1.9771874999999997,
|
||||
"selectedLayer": 0,
|
||||
"viewCenter": {
|
||||
"x": 243.52773826458042,
|
||||
"y": 159.82298087561247
|
||||
}
|
||||
},
|
||||
"write.tmx#sprites": {
|
||||
"scaleInDock": 1
|
||||
}
|
||||
},
|
||||
"last.exportedFilePath": "/home/yuki/dev/walrusfunge/assets",
|
||||
"last.imagePath": "/home/yuki/dev/walrusfunge/assets",
|
||||
"map.height": 10,
|
||||
"map.lastUsedExportFilter": "Fichiers de carte Tiled (*.tmx *.xml)",
|
||||
"map.lastUsedFormat": "tmx",
|
||||
"map.layerDataFormat": "4",
|
||||
"map.width": 15,
|
||||
"openFiles": [
|
||||
"sprites.tsx",
|
||||
"intro.tmx",
|
||||
"test.tmx",
|
||||
"lvl001.tmx",
|
||||
"write.tmx"
|
||||
],
|
||||
"project": "maps.tiled-project",
|
||||
"property.type": "string",
|
||||
"recentFiles": [
|
||||
"sprites.tsx",
|
||||
"write.tmx",
|
||||
"lvl001.tmx",
|
||||
"test.tmx",
|
||||
"intro.tmx",
|
||||
"wrap.tmx",
|
||||
"intro2.tmx"
|
||||
],
|
||||
"textEdit.monospace": true,
|
||||
"tileset.lastUsedFormat": "tsx"
|
||||
}
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.0 KiB |
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.10" tiledversion="1.11.0" name="sprites" tilewidth="32" tileheight="32" tilecount="40" columns="8">
|
||||
<image source="sprites.png" width="256" height="160"/>
|
||||
</tileset>
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="32" tileheight="32" infinite="0" nextlayerid="6" nextobjectid="10">
|
||||
<tileset firstgid="1" name="sprites" tilewidth="32" tileheight="32" tilecount="40" columns="8">
|
||||
<image source="sprites.png" width="256" height="160"/>
|
||||
</tileset>
|
||||
<layer id="1" name="level" class="level" width="30" height="20">
|
||||
<data encoding="base64" compression="zstd">
|
||||
KLUv/WBgCI0BAHgVABUAAQcYCAAGGQoaCQANoEDqA2CDLZcDdR1J7cHXcbkozzSHsCU1k7yZ96ovnSk=
|
||||
</data>
|
||||
</layer>
|
||||
<objectgroup color="#0000ff" id="3" name="testobj">
|
||||
<object id="1" x="38.4337" y="37.4963" width="110.614" height="94.6783" rotation="10"/>
|
||||
<object id="5" x="101.24" y="277.473">
|
||||
<point/>
|
||||
</object>
|
||||
<object id="6" x="99.3653" y="312.157" width="134.987" height="97.4905">
|
||||
<ellipse/>
|
||||
</object>
|
||||
<object id="7" x="422.771" y="77.8049">
|
||||
<polygon points="0,0 160.297,-12.1863 177.17,158.422 120.926,267.161 29.9971,165.921"/>
|
||||
</object>
|
||||
<object id="8" gid="11" x="208.978" y="111.616" width="32" height="32"/>
|
||||
<object id="9" x="277.905" y="92.2165" width="191.305" height="140.848">
|
||||
<text fontfamily="Sans Serif" pixelsize="48" wrap="1" color="#aa00ff">bonjour monde.</text>
|
||||
</object>
|
||||
</objectgroup>
|
||||
<objectgroup id="5" name="player" class="walrus"/>
|
||||
</map>
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="15" height="10" tilewidth="32" tileheight="32" infinite="0" backgroundcolor="#00aa7f" nextlayerid="2" nextobjectid="1">
|
||||
<properties>
|
||||
<property name="name" value="Write test"/>
|
||||
</properties>
|
||||
<tileset firstgid="1" name="sprites" tilewidth="32" tileheight="32" tilecount="40" columns="8">
|
||||
<image source="sprites.png" width="256" height="160"/>
|
||||
</tileset>
|
||||
<layer id="1" name="lvl" class="level,walrus,player" width="15" height="10">
|
||||
<data encoding="base64" compression="zstd">
|
||||
KLUv/WBYAfUBAMQCAAMAAAAXAAAACQAAACMAAAAiAAAAGAAAAAsAAAAHACMACggAAAAlAAAABgAFAAc4FWd2N3iwB6fDoNwV
|
||||
</data>
|
||||
</layer>
|
||||
</map>
|
|
@ -0,0 +1,94 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
// TODO: rewrite as zig tool
|
||||
// TODO: reproducible build
|
||||
const assets = b.addSystemCommand(&[_][]const u8{
|
||||
"tar",
|
||||
"-cf",
|
||||
b.path("assets.tar").getPath(b),
|
||||
"-C",
|
||||
b.path("assets").getPath(b),
|
||||
});
|
||||
assets.addArgs(&[_][]const u8{
|
||||
"write.tmx",
|
||||
"sprites.png",
|
||||
"walrus.png",
|
||||
"player.png",
|
||||
"menu.qoi",
|
||||
"ui.qoi",
|
||||
"Shantell_Sans-Informal_Regular.otf",
|
||||
"Shantell_Sans-Informal_Bold.otf",
|
||||
"cp863.png",
|
||||
});
|
||||
|
||||
const tmx_dep = b.dependency("tmx", .{ .target = target, .optimize = optimize });
|
||||
const tmx = b.addStaticLibrary(.{ .name = "tmx", .target = target, .optimize = optimize });
|
||||
tmx.addCSourceFiles(.{ .root = tmx_dep.path("src/"), .files = &[_][]const u8{ "tmx.c", "tmx_utils.c", "tmx_err.c", "tmx_xml.c", "tmx_mem.c", "tmx_hash.c" }, .flags = &[_][]const u8{ "-Wall", "-O2", "-pthread", "-fno-sanitize=undefined", "-DWANT_ZSTD", "-DWANT_ZLIB" } });
|
||||
tmx.linkLibC();
|
||||
tmx.linkSystemLibrary("z");
|
||||
tmx.linkSystemLibrary("zstd");
|
||||
tmx.linkSystemLibrary2("libxml-2.0", .{ .use_pkg_config = .force });
|
||||
tmx.installHeader(tmx_dep.path("src/tmx.h"), "tmx.h");
|
||||
b.installArtifact(tmx);
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "walrus",
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const strip = b.option(
|
||||
bool,
|
||||
"strip",
|
||||
"Strip debug info to reduce binary size, defaults to false",
|
||||
) orelse false;
|
||||
exe.root_module.strip = strip;
|
||||
|
||||
const useupx = b.option(
|
||||
bool,
|
||||
"upx",
|
||||
"Use UPX to compress the binary further",
|
||||
) orelse false;
|
||||
|
||||
exe.root_module.addAnonymousImport("assets", .{ .root_source_file = b.path("assets.tar") });
|
||||
|
||||
const raylib = b.dependency("raylib", .{ .target = target, .optimize = optimize });
|
||||
// const raylib = @import("raylib");
|
||||
// const raylib_artifact = try raylib.addRaylib(b, target, optimize, .{
|
||||
// .raygui = false,
|
||||
// });
|
||||
//exe.addModule("raylib", raylib.module("raylib"));
|
||||
//exe.installLibraryHeaders(raylib.artifact("raylib"));
|
||||
//exe.installLibraryHeaders(tmx);
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
exe.linkLibC();
|
||||
exe.linkLibrary(raylib.artifact("raylib"));
|
||||
exe.linkLibrary(tmx);
|
||||
exe.step.dependOn(&assets.step);
|
||||
|
||||
if (useupx) {
|
||||
const upx = b.addSystemCommand(&[_][]const u8{ "upx", "--brute", "--no-lzma", "--no-progress" });
|
||||
upx.addArtifactArg(exe);
|
||||
upx.step.dependOn(&exe.step);
|
||||
b.default_step.dependOn(&upx.step);
|
||||
}
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
.{
|
||||
.name = "walrusgame",
|
||||
.version = "0.0.0",
|
||||
.dependencies = .{
|
||||
.raylib = .{
|
||||
.url = "https://github.com/raysan5/raylib/archive/refs/tags/5.5.zip",
|
||||
.hash = "1220d93782859726c2c46a05450615b7edfc82b7319daac50cbc7c3345d660b022d7",
|
||||
},
|
||||
.raygui = .{
|
||||
.url = "https://github.com/raysan5/raygui/archive/1e03efca48c50c5ea4b4a053d5bf04bad58d3e43.zip",
|
||||
.hash = "122062b24f031e68f0d11c91dfc32aed5baf06caf26ed3c80ea1802f9e788ef1c358",
|
||||
},
|
||||
.tmx = .{
|
||||
.url = "https://github.com/baylej/tmx/archive/refs/tags/tmx_1.10.0.zip",
|
||||
.hash = "1220934f743aff07bbbd3557f8c90058367dd589147afd38d08468c3c72740a3fd1e",
|
||||
},
|
||||
},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
"assets",
|
||||
},
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
# The Walrus Game
|
||||
|
||||
The story of a girl teaching her pet walrus to unlock doors.
|
||||
|
||||
Basically, the player have to steer a walrus with blocks towards a door that only opens according to certain conditions. Every block the walrus steps on have special properties, such as changing its direction, managing a FIFO stack of 8-bit integers, and writing to a display. Therefore, this game is theorically Turing-complete.
|
||||
|
||||
## Level design
|
||||
|
||||
### Special layer class names
|
||||
|
||||
Some layers have special properties, the others are drawn, but ignored. The funge spritesheet should be the first one in the list.
|
||||
|
||||
| | |
|
||||
|-|-|
|
||||
|`level`| The tile layer the player and the walrus can interact with and should only use funge sprites. The rest are decorative layers and can contain anything.
|
||||
|`player`| The layer the player is drawn on.
|
||||
|`walrus`| The layer the walrus is drawn on.
|
||||
|`collide`| The blocks can collide with this layer.
|
||||
|
||||
## Walfunge
|
||||
|
||||
Walfunge is the interpreter that powers the walrus. It resembles and is inspired from Befunge. Each block sprite occupying a space on the grid corresponds to an opcode in the interpreter, executed when the walrus steps on it. By default, a block moves the walrus one space forward, but it can also make it change directions, stop until the block is moved away, or jump to another space. It can also modify and do math on a stack, and push characters on a display.
|
||||
|
||||
Some special considerations:
|
||||
|
||||
* The level has a start point (spawn) and an end point (exit). The spawn point is directional, and is removed from play once the level starts, while the exit can be opened or closed according to a win condition. While closed, the exit acts as an empty space. Open, it warps to the next level.
|
||||
* Blocks can be locked in place, or be movable by the player. They can also collide with other blocks and objects.
|
||||
* Some blocks can make the walrus stop until the player moves the block away.
|
||||
* If the interpreter encounters an error, such as a division by zero, the opcode will not execute and the walrus will stop, as if it encountered a stop block.
|
||||
* If the walrus moves outside of the level, it will reappear on the other side.
|
||||
* If the stack or the display reaches its maximum capacity, the interpreter may drop the item at the bottom of the stack to make room for the top.
|
||||
* Display uses code page 863
|
||||
|
||||
### Errors
|
||||
|
||||
* Underflow
|
||||
- The opcode is going to pop more items than what's available in the stack
|
||||
* Division by zero
|
||||
* Out of memory
|
||||
- The stack does not currently have a max capacity, so in the unlikely case you somehow leave the game running on an infinite loop and your OS can't take it anymore it can crash
|
|
@ -0,0 +1,107 @@
|
|||
const std = @import("std");
|
||||
const r = @import("raylib.zig");
|
||||
|
||||
pub const AssetsError = error{
|
||||
FileNotFound,
|
||||
IsDirectory,
|
||||
};
|
||||
|
||||
const assets = @embedFile("assets");
|
||||
|
||||
const allocator = std.heap.page_allocator;
|
||||
|
||||
pub fn get(name: []const u8) ![]u8 {
|
||||
// TODO: check on filesystem first
|
||||
var fbs = std.io.fixedBufferStream(assets);
|
||||
var file_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
var link_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
var iter = std.tar.iterator(fbs.reader(), .{
|
||||
.file_name_buffer = &file_name_buffer,
|
||||
.link_name_buffer = &link_name_buffer,
|
||||
});
|
||||
while (try iter.next()) |file| {
|
||||
switch (file.kind) {
|
||||
.directory => {
|
||||
if (std.mem.eql(u8, file.name, name)) {
|
||||
return error.IsDirectory;
|
||||
}
|
||||
},
|
||||
.file => {
|
||||
if (std.mem.eql(u8, file.name, name)) {
|
||||
//std.debug.print("[Assets] {s}: read {} bytes\n", .{ file.name, file.size });
|
||||
return file.reader().readAllAlloc(allocator, file.size);
|
||||
}
|
||||
},
|
||||
.sym_link => {
|
||||
if (std.mem.eql(u8, file.name, name)) {
|
||||
const actual = try std.fs.path.resolvePosix(allocator, &.{ file.name, file.link_name });
|
||||
defer free(actual);
|
||||
return get(actual);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
return error.FileNotFound;
|
||||
}
|
||||
|
||||
pub fn free(data: anytype) void {
|
||||
allocator.free(data);
|
||||
}
|
||||
|
||||
pub fn getImage(name: []const u8) !r.Image {
|
||||
const a = try get(name);
|
||||
defer free(a);
|
||||
return r.LoadImageFromMemory(std.fs.path.extension(name).ptr, a.ptr, @intCast(a.len));
|
||||
}
|
||||
|
||||
pub fn getTexture(name: []const u8) !r.Texture2D {
|
||||
const i = try getImage(name);
|
||||
defer r.UnloadImage(i);
|
||||
return r.LoadTextureFromImage(i);
|
||||
}
|
||||
|
||||
pub fn getFont(name: []const u8, size: u32) !r.Font {
|
||||
const f = try get(name);
|
||||
defer free(f);
|
||||
return r.LoadFontFromMemory(std.fs.path.extension(name).ptr, f.ptr, @intCast(f.len), @intCast(size), null, 0);
|
||||
}
|
||||
|
||||
pub fn getBitmapFont(name: []const u8, count: r.Vector2, size: r.Vector2, padding: c_int) !r.Font {
|
||||
const cnt: usize = @intFromFloat(count.x * count.y);
|
||||
return r.Font{
|
||||
.baseSize = @intFromFloat(size.y),
|
||||
.glyphCount = @intCast(cnt),
|
||||
.glyphPadding = padding,
|
||||
.texture = try getTexture(name),
|
||||
.recs = blk: {
|
||||
var recs = try allocator.alloc(r.Rectangle, cnt);
|
||||
//defer allocator.free(recs);
|
||||
var i: u32 = 0;
|
||||
while (i < cnt) : (i += 1) {
|
||||
const ii: f32 = @floatFromInt(i);
|
||||
recs[i] = r.Rectangle{
|
||||
.x = @mod(ii, count.x) * size.x,
|
||||
.y = @floor(ii / count.x) * size.y,
|
||||
.width = size.x,
|
||||
.height = size.y,
|
||||
};
|
||||
}
|
||||
break :blk recs.ptr;
|
||||
},
|
||||
.glyphs = blk: {
|
||||
var glyphs = try allocator.alloc(r.GlyphInfo, cnt);
|
||||
//defer allocator.free(glyphs);
|
||||
var i: u32 = 0;
|
||||
while (i < cnt) : (i += 1) {
|
||||
glyphs[i] = r.GlyphInfo{
|
||||
.value = @intCast(i),
|
||||
.offsetX = 0,
|
||||
.offsetY = 0,
|
||||
.advanceX = @intFromFloat(size.x),
|
||||
.image = r.GenImageColor(@intFromFloat(size.x), @intFromFloat(size.y), r.WHITE),
|
||||
};
|
||||
}
|
||||
break :blk glyphs.ptr;
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,231 @@
|
|||
const std = @import("std");
|
||||
const r = @import("raylib.zig");
|
||||
|
||||
const Funge = @This();
|
||||
|
||||
const allocator = std.heap.page_allocator;
|
||||
|
||||
pub const Direction = enum(u32) {
|
||||
South,
|
||||
West,
|
||||
East,
|
||||
North,
|
||||
pub fn spritePosY(self: @This(), offset: f32) f32 {
|
||||
return @as(f32, @floatFromInt(@intFromEnum(self))) * offset;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Opcode = enum(u32) {
|
||||
SpawnSouth,
|
||||
SpawnWest,
|
||||
SpawnEast,
|
||||
SpawnNorth,
|
||||
ExitClosed,
|
||||
ExitOpen,
|
||||
GoSouth,
|
||||
GoWest,
|
||||
GoEast,
|
||||
GoNorth,
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
GreaterThan,
|
||||
Not,
|
||||
Jump,
|
||||
Ok,
|
||||
Stop,
|
||||
Empty,
|
||||
Discard,
|
||||
Digit0,
|
||||
Digit1,
|
||||
Digit2,
|
||||
Digit3,
|
||||
Digit4,
|
||||
Digit5,
|
||||
Digit6,
|
||||
Digit7,
|
||||
Digit8,
|
||||
Digit9,
|
||||
UTurn,
|
||||
Write,
|
||||
Dup,
|
||||
Swap,
|
||||
Horizontal,
|
||||
Vertical,
|
||||
Random,
|
||||
CondStop,
|
||||
_,
|
||||
};
|
||||
|
||||
pub const Action = enum {
|
||||
Stop,
|
||||
Move,
|
||||
Teleport,
|
||||
End,
|
||||
};
|
||||
|
||||
pub const Result = struct {
|
||||
action: Action,
|
||||
teleport: r.Vector2,
|
||||
};
|
||||
|
||||
pub const FungeError = error{
|
||||
Underflow,
|
||||
};
|
||||
|
||||
pub fn init() Funge {
|
||||
return Funge{
|
||||
.stack = std.ArrayList(u8).init(allocator),
|
||||
.display = std.ArrayList(c_int).init(allocator),
|
||||
.direction = .East,
|
||||
.isWin = false,
|
||||
//.winCondition = undefined,
|
||||
.rng = std.rand.DefaultPrng.init(0),
|
||||
};
|
||||
}
|
||||
|
||||
stack: std.ArrayList(u8),
|
||||
display: std.ArrayList(c_int),
|
||||
direction: Direction,
|
||||
isWin: bool,
|
||||
//winCondition: *fn () bool,
|
||||
rng: std.rand.DefaultPrng,
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
self.stack.deinit();
|
||||
self.display.deinit();
|
||||
}
|
||||
|
||||
pub fn checkArgs(self: *@This(), num: u32) !void {
|
||||
if (self.stack.items.len < num) return FungeError.Underflow;
|
||||
}
|
||||
|
||||
pub fn step(self: *@This(), opcode: Opcode) !Result {
|
||||
var res = Result{ .action = .Move, .teleport = .{ .x = 0, .y = 0 } };
|
||||
// if (self.winCondition != undefined) {
|
||||
// if (self.winCondition()) self.isWin = true;
|
||||
// }
|
||||
switch (opcode) {
|
||||
.ExitClosed => {
|
||||
// if (self.winCondition != undefined) {
|
||||
// if (self.winCondition()) res.action = .End;
|
||||
// }
|
||||
if (self.isWin) res.action = .End;
|
||||
},
|
||||
.ExitOpen => res.action = .End,
|
||||
.SpawnEast, .GoEast => self.direction = .East,
|
||||
.SpawnSouth, .GoSouth => self.direction = .South,
|
||||
.SpawnWest, .GoWest => self.direction = .West,
|
||||
.SpawnNorth, .GoNorth => self.direction = .North,
|
||||
.Add => {
|
||||
try self.checkArgs(2);
|
||||
const a = self.stack.pop();
|
||||
const b = self.stack.pop();
|
||||
try self.stack.append(a +% b);
|
||||
},
|
||||
.Sub => {
|
||||
try self.checkArgs(2);
|
||||
const a = self.stack.pop();
|
||||
const b = self.stack.pop();
|
||||
try self.stack.append(b -% a);
|
||||
},
|
||||
.Mul => {
|
||||
try self.checkArgs(2);
|
||||
const a = self.stack.pop();
|
||||
const b = self.stack.pop();
|
||||
try self.stack.append(a *% b);
|
||||
},
|
||||
.Div => {
|
||||
try self.checkArgs(2);
|
||||
const a = self.stack.pop();
|
||||
const b = self.stack.pop();
|
||||
try self.stack.append(try std.math.divTrunc(u8, b, a));
|
||||
},
|
||||
.Mod => {
|
||||
try self.checkArgs(2);
|
||||
const a = self.stack.pop();
|
||||
const b = self.stack.pop();
|
||||
try self.stack.append(try std.math.mod(u8, b, a));
|
||||
},
|
||||
.GreaterThan => {
|
||||
try self.checkArgs(2);
|
||||
const a = self.stack.pop();
|
||||
const b = self.stack.pop();
|
||||
try self.stack.append(if (b > a) 1 else 0);
|
||||
},
|
||||
.Not => {
|
||||
try self.checkArgs(1);
|
||||
const a = self.stack.pop();
|
||||
try self.stack.append(if (a == 0) 1 else 0);
|
||||
},
|
||||
.Jump => {
|
||||
res.action = .Teleport;
|
||||
res.teleport = r.Vector2{
|
||||
.x = switch (self.direction) {
|
||||
.East => 2,
|
||||
.North, .South => 0,
|
||||
.West => -2,
|
||||
},
|
||||
.y = switch (self.direction) {
|
||||
.South => 2,
|
||||
.West, .East => 0,
|
||||
.North => -2,
|
||||
},
|
||||
};
|
||||
},
|
||||
.Ok => self.isWin = true,
|
||||
.Stop => res.action = .Stop,
|
||||
.Empty => try self.stack.resize(0),
|
||||
.Discard => {
|
||||
try self.checkArgs(1);
|
||||
_ = self.stack.pop();
|
||||
},
|
||||
.Digit0 => try self.stack.append(0),
|
||||
.Digit1 => try self.stack.append(1),
|
||||
.Digit2 => try self.stack.append(2),
|
||||
.Digit3 => try self.stack.append(3),
|
||||
.Digit4 => try self.stack.append(4),
|
||||
.Digit5 => try self.stack.append(5),
|
||||
.Digit6 => try self.stack.append(6),
|
||||
.Digit7 => try self.stack.append(7),
|
||||
.Digit8 => try self.stack.append(8),
|
||||
.Digit9 => try self.stack.append(9),
|
||||
.UTurn => self.direction = switch (self.direction) {
|
||||
.West => .East,
|
||||
.East => .West,
|
||||
.North => .South,
|
||||
.South => .North,
|
||||
},
|
||||
.Write => {
|
||||
try self.checkArgs(1);
|
||||
const c = self.stack.pop();
|
||||
//if (c > 0)
|
||||
try self.display.append(c);
|
||||
},
|
||||
.Dup => {
|
||||
try self.checkArgs(1);
|
||||
const c = self.stack.getLast();
|
||||
try self.stack.append(c);
|
||||
},
|
||||
.Horizontal => {
|
||||
try self.checkArgs(1);
|
||||
const c = self.stack.pop();
|
||||
self.direction = if (c == 0) .East else .West;
|
||||
},
|
||||
.Vertical => {
|
||||
try self.checkArgs(1);
|
||||
const c = self.stack.pop();
|
||||
self.direction = if (c == 0) .South else .North;
|
||||
},
|
||||
.Random => self.direction = self.rng.random().enumValue(Direction),
|
||||
.CondStop => {
|
||||
try self.checkArgs(1);
|
||||
const c = self.stack.pop();
|
||||
if (c == 0) res.action = .Stop;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return res;
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
const std = @import("std");
|
||||
const r = @import("raylib.zig");
|
||||
const Walrus = @import("walrus.zig");
|
||||
const Map = @import("map.zig");
|
||||
const Menu = @import("menu.zig");
|
||||
|
||||
const Screen = enum {
|
||||
MainMenu,
|
||||
Game,
|
||||
PauseMenu,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
r.SetTraceLogLevel(r.LOG_WARNING);
|
||||
r.SetConfigFlags(r.FLAG_WINDOW_RESIZABLE | r.FLAG_FULLSCREEN_MODE);
|
||||
r.InitWindow(1920, 1080, "Where's the wlarus?");
|
||||
r.SetTargetFPS(60);
|
||||
r.SetExitKey(0);
|
||||
defer r.CloseWindow();
|
||||
|
||||
var screen = Screen.MainMenu;
|
||||
|
||||
var menu = try Menu.init();
|
||||
defer menu.deinit();
|
||||
|
||||
var map = try Map.init("write.tmx");
|
||||
defer map.deinit();
|
||||
|
||||
var quit = false;
|
||||
|
||||
var rt = r.LoadRenderTexture(1920, 1080);
|
||||
defer r.UnloadRenderTexture(rt);
|
||||
|
||||
while (!r.WindowShouldClose() and !quit) {
|
||||
{
|
||||
switch (screen) {
|
||||
.MainMenu => {
|
||||
if (menu.update()) {
|
||||
switch (menu.current) {
|
||||
.Start => screen = .Game,
|
||||
.Options => {},
|
||||
.Credits => {},
|
||||
.Quit => quit = true,
|
||||
}
|
||||
}
|
||||
if (r.IsKeyPressed(r.KEY_ESCAPE)) {
|
||||
quit = true;
|
||||
}
|
||||
r.ClearBackground(r.BLACK);
|
||||
menu.draw();
|
||||
rt = menu.render;
|
||||
},
|
||||
.Game => {
|
||||
map.update();
|
||||
if (r.IsKeyPressed(r.KEY_ESCAPE)) {
|
||||
screen = .MainMenu;
|
||||
}
|
||||
r.ClearBackground(r.BLUE);
|
||||
map.draw();
|
||||
rt = map.render;
|
||||
},
|
||||
.PauseMenu => {},
|
||||
}
|
||||
}
|
||||
{
|
||||
r.BeginDrawing();
|
||||
defer r.EndDrawing();
|
||||
|
||||
const w: f32 = @floatFromInt(r.GetScreenWidth());
|
||||
const h: f32 = @floatFromInt(r.GetScreenHeight());
|
||||
|
||||
r.DrawTexturePro(
|
||||
rt.texture,
|
||||
r.Rectangle{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = @floatFromInt(rt.texture.width),
|
||||
.height = @floatFromInt(-rt.texture.height),
|
||||
},
|
||||
r.Rectangle{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = w,
|
||||
.height = h,
|
||||
},
|
||||
r.Vector2{ .x = 0, .y = 0 },
|
||||
0,
|
||||
r.WHITE,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,379 @@
|
|||
const std = @import("std");
|
||||
const r = @import("raylib.zig");
|
||||
const tmx = @import("tmx.zig");
|
||||
const assets = @import("assets.zig");
|
||||
const Walrus = @import("walrus.zig");
|
||||
const Funge = @import("funge.zig");
|
||||
|
||||
const Map = @This();
|
||||
|
||||
const allocator = std.heap.page_allocator;
|
||||
|
||||
pub const MapError = error{
|
||||
CannotLoadMap,
|
||||
};
|
||||
|
||||
pub fn init(name: []const u8) !Map {
|
||||
const data = try assets.get(name);
|
||||
//std.debug.print("[Map] read {} bytes\n", .{data.len});
|
||||
defer assets.free(data);
|
||||
tmx.tmx_img_load_func = tmx.texLoad;
|
||||
tmx.tmx_img_free_func = tmx.texFree;
|
||||
const map = tmx.tmx_load_buffer(data.ptr, @as(c_int, @intCast(data.len)));
|
||||
if (map == null) {
|
||||
std.debug.print("[Map] error {}: {s}\n", .{ tmx.tmx_errno, tmx.tmx_strerr() });
|
||||
return MapError.CannotLoadMap;
|
||||
}
|
||||
const sw: f32 = 1920; //@floatFromInt(r.GetScreenWidth());
|
||||
var m = Map{
|
||||
.render = r.LoadRenderTexture(sw, 1080),
|
||||
.map = map,
|
||||
.font = try assets.getFont("Shantell_Sans-Informal_Regular.otf", 64),
|
||||
.dispfont = try assets.getBitmapFont(
|
||||
"cp863.png",
|
||||
r.Vector2{ .x = 16, .y = 16 },
|
||||
r.Vector2{ .x = 9, .y = 16 },
|
||||
0,
|
||||
),
|
||||
.camera = r.Camera2D{
|
||||
.target = r.Vector2{ .x = -sw * 0.25, .y = 0 },
|
||||
.offset = r.Vector2{ .x = 0, .y = 0 },
|
||||
.rotation = 0.0,
|
||||
.zoom = 2.0,
|
||||
},
|
||||
.walrus = try Walrus.init("walrus.png"),
|
||||
.player = try Walrus.init("player.png"),
|
||||
.ui = try assets.getTexture("ui.qoi"),
|
||||
.funge = Funge.init(),
|
||||
};
|
||||
const width: u32 = m.map.*.width;
|
||||
const height: u32 = m.map.*.height;
|
||||
m.walrus.offset = r.Vector2{ .x = @floatFromInt(m.map.*.tile_width), .y = @floatFromInt(m.map.*.tile_height) };
|
||||
m.walrus.bounds = r.Rectangle{ .x = 0, .y = 0, .width = @floatFromInt(width * m.map.*.tile_width), .height = @floatFromInt(height * m.map.*.tile_height) };
|
||||
m.player.frames = 4;
|
||||
m.player.size = r.Rectangle{ .x = 0, .y = -20, .width = 32, .height = 48 };
|
||||
m.player.bounds = m.walrus.bounds;
|
||||
const level = m.getLayerFromClass("level");
|
||||
if (level != null) {
|
||||
if (level.*.type == tmx.L_LAYER) {
|
||||
var i: u32 = 0;
|
||||
while (i < height * width) : (i += 1) {
|
||||
const ti = i / width;
|
||||
const tj = i % width;
|
||||
const gid = level.*.content.gids[i] & tmx.TMX_FLIP_BITS_REMOVAL;
|
||||
if (m.map.*.tiles[gid] != null) {
|
||||
const t = m.map.*.tiles[gid].*;
|
||||
if (t.id < 4) {
|
||||
const ts = t.tileset.*;
|
||||
const w: f32 = @floatFromInt(ts.tile_width);
|
||||
const h: f32 = @floatFromInt(ts.tile_height);
|
||||
const fi = @as(f32, @floatFromInt(ti)) * h;
|
||||
const fj = @as(f32, @floatFromInt(tj)) * w;
|
||||
m.walrus.spawn(r.Vector2{ .x = fj, .y = fi }, @enumFromInt(t.id));
|
||||
m.funge.direction = m.walrus.dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
render: r.RenderTexture2D,
|
||||
map: [*c]tmx.tmx_map,
|
||||
camera: r.Camera2D,
|
||||
font: r.Font,
|
||||
dispfont: r.Font,
|
||||
walrus: Walrus,
|
||||
player: Walrus,
|
||||
ui: r.Texture2D,
|
||||
funge: Funge,
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
tmx.tmx_map_free(self.map);
|
||||
r.UnloadFont(self.font);
|
||||
//r.UnloadFont(self.dispfont);
|
||||
r.UnloadTexture(self.ui);
|
||||
self.walrus.deinit();
|
||||
self.funge.deinit();
|
||||
r.UnloadRenderTexture(self.render);
|
||||
}
|
||||
|
||||
pub fn update(self: *@This()) void {
|
||||
const pos = r.GetMousePosition();
|
||||
const sw: f32 = @floatFromInt(self.render.texture.width); //@floatFromInt(r.GetScreenWidth());
|
||||
const sh: f32 = @floatFromInt(self.render.texture.height); //sw * 9.0 / 16.0; //@floatFromInt(r.GetScreenHeight());
|
||||
if (r.IsMouseButtonDown(0) and pos.x / sw > 0.2 and pos.y / sh < 1) {
|
||||
const d = r.GetMouseDelta();
|
||||
self.camera.target.x -= d.x;
|
||||
self.camera.target.y -= d.y;
|
||||
}
|
||||
const width: u32 = self.map.*.width;
|
||||
const height: u32 = self.map.*.height;
|
||||
const tw: u32 = self.map.*.tile_width;
|
||||
const th: u32 = self.map.*.tile_height;
|
||||
const pw: f32 = @floatFromInt(width * tw / 2);
|
||||
const ph: f32 = @floatFromInt(height * th / 2);
|
||||
if (self.camera.target.x > pw - sw) self.camera.target.x = pw - sw;
|
||||
if (self.camera.target.x < -0.1 * sw) self.camera.target.x = -0.1 * sw;
|
||||
if (self.camera.target.y > ph - sh) self.camera.target.y = ph - sh;
|
||||
if (self.camera.target.y < 0) self.camera.target.y = 0;
|
||||
if (!self.player.isMoving) {
|
||||
if (r.IsKeyDown(r.KEY_LEFT)) {
|
||||
self.player.moveDir(.West);
|
||||
} else if (r.IsKeyDown(r.KEY_UP)) {
|
||||
self.player.moveDir(.North);
|
||||
} else if (r.IsKeyDown(r.KEY_RIGHT)) {
|
||||
self.player.moveDir(.East);
|
||||
} else if (r.IsKeyDown(r.KEY_DOWN)) {
|
||||
self.player.moveDir(.South);
|
||||
}
|
||||
}
|
||||
if (!self.walrus.isMoving) {
|
||||
const level = self.getLayerFromClass("level");
|
||||
if (level != null) {
|
||||
if (level.*.type == tmx.L_LAYER) {
|
||||
const x: u32 = @intFromFloat(self.walrus.pos.x / self.walrus.offset.x);
|
||||
const y: u32 = @intFromFloat(self.walrus.pos.y / self.walrus.offset.y);
|
||||
const i: u32 = y * width + x;
|
||||
const gid = level.*.content.gids[i] & tmx.TMX_FLIP_BITS_REMOVAL;
|
||||
var result = Funge.Result{ .action = .Move, .teleport = undefined };
|
||||
if (self.map.*.tiles[gid] != null) {
|
||||
const opcode: Funge.Opcode = @enumFromInt(self.map.*.tiles[gid].*.id);
|
||||
result = self.funge.step(opcode) catch Funge.Result{ .action = .Stop, .teleport = undefined };
|
||||
}
|
||||
self.walrus.dir = self.funge.direction;
|
||||
switch (result.action) {
|
||||
.Stop, .End => {},
|
||||
.Move => {
|
||||
self.walrus.move();
|
||||
},
|
||||
.Teleport => {
|
||||
self.walrus.moveRel(result.teleport);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.player.update();
|
||||
self.walrus.update();
|
||||
}
|
||||
|
||||
pub fn draw(self: *@This()) void {
|
||||
r.BeginTextureMode(self.render);
|
||||
defer r.EndTextureMode();
|
||||
|
||||
r.ClearBackground(r.getTmxColor(self.map.*.backgroundcolor));
|
||||
r.BeginMode2D(self.camera);
|
||||
self.drawAllLayers(self.map.*.ly_head);
|
||||
r.EndMode2D();
|
||||
const sh: f32 = @floatFromInt(self.render.texture.height); //@floatFromInt(r.GetScreenHeight());
|
||||
const th: f32 = @floatFromInt(self.ui.height);
|
||||
r.DrawTextureEx(self.ui, .{ .x = 0, .y = 0 }, 0, sh / th, r.WHITE);
|
||||
const title = tmx.getProperty(self.map.*.properties, "name");
|
||||
if (title != .none) {
|
||||
r.DrawTextCenteredEx(
|
||||
self.font,
|
||||
title.string.ptr,
|
||||
r.Vector2{ .x = 192, .y = 48 },
|
||||
48,
|
||||
0,
|
||||
r.WHITE,
|
||||
);
|
||||
}
|
||||
r.DrawTextCenteredEx(
|
||||
self.font,
|
||||
"Restart",
|
||||
r.Vector2{ .x = 192, .y = 144 },
|
||||
48,
|
||||
0,
|
||||
r.WHITE,
|
||||
);
|
||||
if (self.funge.stack.items.len > 0) {
|
||||
var i = self.funge.stack.items.len;
|
||||
var j: usize = 0;
|
||||
while (i > 0 and j < 8) : (i -= 1) {
|
||||
const text = std.fmt.allocPrint(allocator, "{: >3}", .{self.funge.stack.items[i - 1]}) catch "0";
|
||||
defer allocator.free(text);
|
||||
r.DrawTextEx(
|
||||
self.dispfont,
|
||||
text.ptr,
|
||||
r.Vector2{ .x = 16, .y = 296 - @as(f32, @floatFromInt(i)) * 64 + 64 * @as(f32, @floatFromInt(self.funge.stack.items.len)) },
|
||||
64,
|
||||
0,
|
||||
r.BLACK,
|
||||
);
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
const str = self.funge.display.items.ptr;
|
||||
r.DrawTextCodepoints(
|
||||
self.dispfont,
|
||||
str,
|
||||
@intCast(self.funge.display.items.len),
|
||||
r.Vector2{ .x = 16, .y = 810 },
|
||||
64,
|
||||
0,
|
||||
r.BLACK,
|
||||
);
|
||||
}
|
||||
|
||||
fn getLayerFromClass(self: *@This(), name: []const u8) [*c]tmx.tmx_layer {
|
||||
return self.getSublayerFromClass(name, self.map.*.ly_head);
|
||||
}
|
||||
|
||||
fn getSublayerFromClass(self: *@This(), name: []const u8, layer: [*c]tmx.tmx_layer) [*c]tmx.tmx_layer {
|
||||
var l = layer;
|
||||
while (l != null) : (l = l.*.next) {
|
||||
if (l.*.type == tmx.L_GROUP) {
|
||||
const ll = self.getSublayerFromClass(name, l.*.content.group_head);
|
||||
if (ll != null) return ll;
|
||||
}
|
||||
if (l.*.class_type != null) {
|
||||
var split = std.mem.splitSequence(u8, std.mem.span(l.*.class_type), ",");
|
||||
while (split.next()) |str| {
|
||||
if (std.mem.eql(u8, str, name)) {
|
||||
return l;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn drawAllLayers(self: *@This(), layer: [*c]tmx.tmx_layer) void {
|
||||
var l = layer;
|
||||
while (l != null) : (l = l.*.next) {
|
||||
if (l.*.visible != 0) {
|
||||
const op = @as(u8, @intFromFloat(l.*.opacity * 255));
|
||||
const opc = r.Color{ .r = op, .g = op, .b = op, .a = op };
|
||||
const origin = r.Vector2{
|
||||
.x = @floatFromInt(l.*.offsetx),
|
||||
.y = @floatFromInt(l.*.offsety),
|
||||
};
|
||||
switch (l.*.type) {
|
||||
tmx.L_NONE => {},
|
||||
tmx.L_GROUP => {
|
||||
self.drawAllLayers(l.*.content.group_head);
|
||||
},
|
||||
tmx.L_OBJGR => {
|
||||
const objgr = l.*.content.objgr.*;
|
||||
var head = objgr.head;
|
||||
const color = r.getTmxColor(objgr.color);
|
||||
while (head != null) : (head = head.*.next) {
|
||||
const h = head.*;
|
||||
if (h.visible != 0) {
|
||||
switch (h.obj_type) {
|
||||
tmx.OT_NONE => {},
|
||||
tmx.OT_SQUARE => {
|
||||
r.DrawRectanglePro(
|
||||
r.Rectangle{
|
||||
.x = @floatCast(h.x),
|
||||
.y = @floatCast(h.y),
|
||||
.width = @floatCast(h.width),
|
||||
.height = @floatCast(h.height),
|
||||
},
|
||||
r.Vector2{ .x = 0, .y = 0 },
|
||||
@floatCast(h.rotation),
|
||||
color,
|
||||
);
|
||||
},
|
||||
tmx.OT_POLYGON => {
|
||||
// TODO
|
||||
},
|
||||
tmx.OT_POLYLINE => {
|
||||
// TODO
|
||||
},
|
||||
tmx.OT_ELLIPSE => {
|
||||
r.DrawEllipse(
|
||||
@intFromFloat(h.x + h.width / 2.0),
|
||||
@intFromFloat(h.y + h.height / 2.0),
|
||||
@floatCast(h.width / 2.0),
|
||||
@floatCast(h.height / 2.0),
|
||||
color,
|
||||
);
|
||||
},
|
||||
tmx.OT_TILE => {
|
||||
self.drawTile(
|
||||
@intCast(h.content.gid),
|
||||
@intFromFloat(h.y),
|
||||
@intFromFloat(h.x),
|
||||
op,
|
||||
false,
|
||||
);
|
||||
},
|
||||
tmx.OT_TEXT => {
|
||||
const text = h.content.text.*;
|
||||
r.DrawTextPro(
|
||||
self.font,
|
||||
text.text,
|
||||
r.Vector2{ .x = @floatCast(h.x), .y = @floatCast(h.y) },
|
||||
origin,
|
||||
@floatCast(h.rotation),
|
||||
@floatFromInt(text.pixelsize),
|
||||
@floatFromInt(text.kerning),
|
||||
r.getTmxColor(text.color),
|
||||
);
|
||||
},
|
||||
tmx.OT_POINT => {},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tmx.L_IMAGE => {
|
||||
const tex = @as(*r.Texture2D, @alignCast(@ptrCast(l.*.content.image.*.resource_image)));
|
||||
r.DrawTexture(tex.*, l.*.offsetx, l.*.offsety, opc);
|
||||
},
|
||||
tmx.L_LAYER => {
|
||||
var i: u32 = 0;
|
||||
const width: u32 = self.map.*.width;
|
||||
const height: u32 = self.map.*.height;
|
||||
while (i < height * width) : (i += 1) {
|
||||
const ti = i / width;
|
||||
const tj = i % width;
|
||||
const gid = l.*.content.gids[i] & tmx.TMX_FLIP_BITS_REMOVAL;
|
||||
self.drawTile(gid, ti, tj, op, true);
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
if (l.*.class_type != null) {
|
||||
var split = std.mem.splitSequence(u8, std.mem.span(l.*.class_type), ",");
|
||||
while (split.next()) |str| {
|
||||
if (std.mem.eql(u8, str, "walrus")) {
|
||||
self.walrus.draw();
|
||||
}
|
||||
if (std.mem.eql(u8, str, "player")) {
|
||||
self.player.draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn drawTile(self: *@This(), gid: u32, i: u32, j: u32, op: u8, grid: bool) void {
|
||||
if (self.map.*.tiles[gid] != null) {
|
||||
const t = self.map.*.tiles[gid].*;
|
||||
const ts = t.tileset.*;
|
||||
const x: f32 = @floatFromInt(t.ul_x);
|
||||
const y: f32 = @floatFromInt(t.ul_y);
|
||||
const w: f32 = @floatFromInt(ts.tile_width);
|
||||
const h: f32 = @floatFromInt(ts.tile_height);
|
||||
const fi = @as(f32, @floatFromInt(i)) * if (grid) h else 1;
|
||||
const fj = @as(f32, @floatFromInt(j)) * if (grid) w else 1;
|
||||
var image: *r.Texture2D = undefined;
|
||||
if (t.image != null) {
|
||||
image = @as(*r.Texture2D, @alignCast(@ptrCast(t.image.*.resource_image)));
|
||||
} else {
|
||||
image = @as(*r.Texture2D, @alignCast(@ptrCast(ts.image.*.resource_image)));
|
||||
}
|
||||
r.DrawTextureRec(
|
||||
image.*,
|
||||
r.Rectangle{ .x = x, .y = y, .width = w, .height = h },
|
||||
r.Vector2{ .x = fj, .y = fi },
|
||||
r.Color{ .r = op, .g = op, .b = op, .a = op },
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
const std = @import("std");
|
||||
const r = @import("raylib.zig");
|
||||
const assets = @import("assets.zig");
|
||||
|
||||
const Menu = @This();
|
||||
|
||||
const MenuOptions = enum(u32) {
|
||||
Start,
|
||||
Options,
|
||||
Credits,
|
||||
Quit,
|
||||
};
|
||||
|
||||
pub fn init() !Menu {
|
||||
return Menu{
|
||||
.render = r.LoadRenderTexture(1920, 1080),
|
||||
.menutex = try assets.getTexture("menu.qoi"),
|
||||
.messages = &[_][]const u8{
|
||||
"start",
|
||||
"options",
|
||||
"credits",
|
||||
"quit",
|
||||
},
|
||||
.font = try assets.getFont("Shantell_Sans-Informal_Regular.otf", 64),
|
||||
.boldfont = try assets.getFont("Shantell_Sans-Informal_Bold.otf", 64),
|
||||
};
|
||||
}
|
||||
|
||||
render: r.RenderTexture2D,
|
||||
menutex: r.Texture2D,
|
||||
current: MenuOptions = .Start,
|
||||
messages: []const []const u8,
|
||||
font: r.Font,
|
||||
boldfont: r.Font,
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
r.UnloadTexture(self.menutex);
|
||||
r.UnloadFont(self.font);
|
||||
r.UnloadFont(self.boldfont);
|
||||
r.UnloadRenderTexture(self.render);
|
||||
}
|
||||
|
||||
pub fn update(self: *@This()) bool {
|
||||
const max = std.meta.fields(MenuOptions).len - 1;
|
||||
if (r.IsKeyPressed(r.KEY_UP)) {
|
||||
if (@intFromEnum(self.current) > 0) {
|
||||
self.current = @enumFromInt(@intFromEnum(self.current) - 1);
|
||||
} else {
|
||||
self.current = @enumFromInt(max);
|
||||
}
|
||||
} else if (r.IsKeyPressed(r.KEY_DOWN)) {
|
||||
if (@intFromEnum(self.current) < max) {
|
||||
self.current = @enumFromInt(@intFromEnum(self.current) + 1);
|
||||
} else {
|
||||
self.current = @enumFromInt(0);
|
||||
}
|
||||
}
|
||||
var clicked = false;
|
||||
var i: usize = 0;
|
||||
const fontsize: f32 = @floatFromInt(self.font.baseSize);
|
||||
const sw: f32 = @floatFromInt(self.render.texture.width);
|
||||
const sh: f32 = @floatFromInt(self.render.texture.height);
|
||||
while (i < self.messages.len) : (i += 1) {
|
||||
const font = if (i == @intFromEnum(self.current)) self.boldfont else self.font;
|
||||
const ts = r.MeasureTextEx(font, self.messages[i].ptr, fontsize, 0);
|
||||
const x = sw / 2 - ts.x / 2;
|
||||
const y = sh * 0.4 + fontsize * 1.25 * @as(f32, @floatFromInt(i));
|
||||
const mpos = r.GetMousePosition();
|
||||
if (x <= mpos.x and mpos.x <= x + ts.x and
|
||||
y <= mpos.y and mpos.y <= y + ts.y)
|
||||
{
|
||||
self.current = @enumFromInt(i);
|
||||
if (r.IsMouseButtonPressed(0)) {
|
||||
clicked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return r.IsKeyPressed(r.KEY_ENTER) or clicked;
|
||||
}
|
||||
|
||||
pub fn draw(self: *@This()) void {
|
||||
r.BeginTextureMode(self.render);
|
||||
defer r.EndTextureMode();
|
||||
|
||||
const fontsize: f32 = @floatFromInt(self.font.baseSize);
|
||||
const sw: f32 = @floatFromInt(self.render.texture.width);
|
||||
const sh: f32 = @floatFromInt(self.render.texture.height);
|
||||
const tw: f32 = @floatFromInt(self.menutex.width);
|
||||
//const th: f32 = @floatFromInt(self.menutex.height);
|
||||
r.DrawTextureEx(self.menutex, .{ .x = 0, .y = 0 }, 0, sw / tw, r.WHITE);
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < self.messages.len) : (i += 1) {
|
||||
const font = if (i == @intFromEnum(self.current)) self.boldfont else self.font;
|
||||
const ts = r.MeasureTextEx(font, self.messages[i].ptr, fontsize, 0);
|
||||
const x = sw / 2 - ts.x / 2;
|
||||
const y = sh * 0.4 + fontsize * 1.25 * @as(f32, @floatFromInt(i));
|
||||
r.DrawTextEx(
|
||||
font,
|
||||
self.messages[i].ptr,
|
||||
r.Vector2{ .x = x, .y = y },
|
||||
fontsize,
|
||||
0,
|
||||
if (i == @intFromEnum(self.current)) r.Color{ .r = 0x80, .g = 0, .b = 0xFF, .a = 0xFF } else r.WHITE,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub usingnamespace @cImport({
|
||||
@cInclude("raylib.h");
|
||||
// @cDefine("RAYGUI_IMPLEMENTATION", {});
|
||||
// @cInclude("raygui.h");
|
||||
});
|
||||
|
||||
const r = @This();
|
||||
|
||||
pub fn DrawTextCenteredEx(font: r.Font, text: [*c]const u8, position: r.Vector2, fontSize: f32, spacing: f32, tint: r.Color) void {
|
||||
const offset = r.MeasureTextEx(font, text, fontSize, spacing);
|
||||
r.DrawTextEx(
|
||||
font,
|
||||
text,
|
||||
r.Vector2{ .x = position.x - offset.x / 2, .y = position.y - offset.y / 2 },
|
||||
fontSize,
|
||||
spacing,
|
||||
tint,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn getTmxColor(color: u32) r.Color {
|
||||
return r.GetColor(std.math.rotl(u32, color, 8));
|
||||
}
|
||||
|
||||
// const Allocator = std.mem.Allocator;
|
||||
// const RaylibAllocator = struct {
|
||||
// ptr: *anyopaque,
|
||||
// pub fn init() Allocator {
|
||||
// return Allocator{
|
||||
// .ptr = undefined,
|
||||
// .vtable = &Allocator.VTable{
|
||||
// .alloc = alloc,
|
||||
// .free = undefined,
|
||||
// .resize = undefined,
|
||||
// },
|
||||
// };
|
||||
// }
|
||||
// fn alloc(_: *anyopaque, len: usize, _: u8, _: usize) ?*anyopaque {
|
||||
// return r.MemAlloc(len);
|
||||
// }
|
||||
// fn free(_: *anyopaque, buf: []u8, _: u8, _: usize) void {
|
||||
// return r.MemFree(buf);
|
||||
// }
|
||||
// fn resize(_: *anyopaque, buf: []u8, _: u8, new_size: usize, _: usize) void {
|
||||
// return r.MemRealloc(buf, new_size);
|
||||
// }
|
||||
// };
|
|
@ -0,0 +1,56 @@
|
|||
const std = @import("std");
|
||||
const r = @import("raylib.zig");
|
||||
const assets = @import("assets.zig");
|
||||
|
||||
pub usingnamespace @cImport(@cInclude("tmx.h"));
|
||||
|
||||
const tmx = @This();
|
||||
|
||||
pub fn texLoad(path: [*c]const u8) callconv(.C) ?*anyopaque {
|
||||
const data = assets.getTexture(std.mem.span(path)) catch return null;
|
||||
const tex: *r.Texture2D = std.heap.page_allocator.create(r.Texture2D) catch return null;
|
||||
tex.* = data;
|
||||
return @as(?*anyopaque, @ptrCast(tex));
|
||||
}
|
||||
|
||||
pub fn texFree(tex: ?*anyopaque) callconv(.C) void {
|
||||
if (tex != null) {
|
||||
const t = @as(*r.Texture2D, @alignCast(@ptrCast(tex.?)));
|
||||
r.UnloadTexture(t.*);
|
||||
std.heap.page_allocator.destroy(t);
|
||||
}
|
||||
}
|
||||
|
||||
pub const Property = union(enum) {
|
||||
none,
|
||||
integer: i32,
|
||||
decimal: f32,
|
||||
boolean: bool,
|
||||
string: []u8,
|
||||
color: r.Color,
|
||||
file: []u8,
|
||||
object_id: c_int,
|
||||
//properties: tmx.str,
|
||||
};
|
||||
|
||||
// pub fn getProperties(hash: ?*tmx.tmx_properties) []Property{
|
||||
// const props = tmx.tmx_property_foreach(hash: ?*tmx_properties, callback: tmx_property_functor, userdata: ?*anyopaque)
|
||||
// }
|
||||
|
||||
pub fn getProperty(hash: ?*tmx.tmx_properties, key: [*c]const u8) Property {
|
||||
const prop = tmx.tmx_get_property(hash, key);
|
||||
if (prop == null) return .none;
|
||||
const ret: Property = switch (prop.*.type) {
|
||||
tmx.PT_NONE => .none,
|
||||
tmx.PT_INT => Property{ .integer = @intCast(prop.*.value.integer) },
|
||||
tmx.PT_FLOAT => Property{ .decimal = prop.*.value.decimal },
|
||||
tmx.PT_BOOL => Property{ .boolean = prop.*.value.boolean == 0 },
|
||||
tmx.PT_STRING => Property{ .string = std.mem.span(prop.*.value.string) },
|
||||
tmx.PT_COLOR => Property{ .color = r.getTmxColor(prop.*.value.color) },
|
||||
tmx.PT_FILE => Property{ .file = assets.get(std.mem.span(prop.*.value.file)) catch "" },
|
||||
tmx.PT_OBJECT => Property{ .object_id = prop.*.value.object_id },
|
||||
tmx.PT_CUSTOM => .none, //ret.properties = prop.*.value.properties,
|
||||
else => .none,
|
||||
};
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
const std = @import("std");
|
||||
const r = @import("raylib.zig");
|
||||
const assets = @import("assets.zig");
|
||||
const Funge = @import("funge.zig");
|
||||
|
||||
const Walrus = @This();
|
||||
|
||||
pub const Direction = Funge.Direction;
|
||||
|
||||
pub fn init(name: []const u8) !Walrus {
|
||||
const tex = try assets.getTexture(name);
|
||||
const pos = r.Vector2{ .x = 0, .y = 0 };
|
||||
return Walrus{
|
||||
.tex = tex,
|
||||
.dir = .East,
|
||||
.pos = pos,
|
||||
.actualpos = pos,
|
||||
.offset = r.Vector2{ .x = 32, .y = 32 },
|
||||
.isMoving = false,
|
||||
.bounds = r.Rectangle{ .x = 0, .y = 0, .width = 640, .height = 480 },
|
||||
.size = r.Rectangle{ .x = 4, .y = 4, .width = 24, .height = 24 },
|
||||
.frames = 2,
|
||||
};
|
||||
}
|
||||
|
||||
tex: r.Texture2D,
|
||||
dir: Direction,
|
||||
pos: r.Vector2,
|
||||
actualpos: r.Vector2,
|
||||
offset: r.Vector2,
|
||||
isMoving: bool,
|
||||
bounds: r.Rectangle,
|
||||
size: r.Rectangle,
|
||||
frames: f32,
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
r.UnloadTexture(self.tex);
|
||||
}
|
||||
|
||||
pub fn spawn(self: *@This(), pos: r.Vector2, dir: Direction) void {
|
||||
self.pos = pos;
|
||||
self.actualpos = pos;
|
||||
self.dir = dir;
|
||||
}
|
||||
|
||||
pub fn correctBounds(self: *@This(), pos: r.Vector2) r.Vector2 {
|
||||
return r.Vector2{
|
||||
.x = @mod(pos.x - self.bounds.x, self.bounds.width) + self.bounds.x,
|
||||
.y = @mod(pos.y - self.bounds.y, self.bounds.height) + self.bounds.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn update(self: *@This()) void {
|
||||
if (self.isMoving) {
|
||||
switch (self.dir) {
|
||||
.West => {
|
||||
self.actualpos.x -= 1;
|
||||
},
|
||||
.North => {
|
||||
self.actualpos.y -= 1;
|
||||
},
|
||||
.East => {
|
||||
self.actualpos.x += 1;
|
||||
},
|
||||
.South => {
|
||||
self.actualpos.y += 1;
|
||||
},
|
||||
}
|
||||
self.actualpos = self.correctBounds(self.actualpos);
|
||||
if (self.pos.x == self.actualpos.x and self.pos.y == self.actualpos.y) self.isMoving = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn moveDir(self: *@This(), direction: Direction) void {
|
||||
self.dir = direction;
|
||||
self.move();
|
||||
}
|
||||
|
||||
pub fn move(self: *@This()) void {
|
||||
self.isMoving = true;
|
||||
switch (self.dir) {
|
||||
.West => self.pos.x -= self.offset.x,
|
||||
.North => self.pos.y -= self.offset.y,
|
||||
.East => self.pos.x += self.offset.x,
|
||||
.South => self.pos.y += self.offset.y,
|
||||
}
|
||||
self.pos = self.correctBounds(self.pos);
|
||||
}
|
||||
|
||||
pub fn moveRel(self: *@This(), pos: r.Vector2) void {
|
||||
self.pos.x += self.offset.x * pos.x;
|
||||
self.pos.y += self.offset.y * pos.y;
|
||||
self.pos = self.correctBounds(self.pos);
|
||||
self.actualpos = self.pos;
|
||||
}
|
||||
|
||||
pub fn moveAbs(self: *@This(), pos: r.Vector2) void {
|
||||
self.pos.x = self.offset.x * pos.x;
|
||||
self.pos.y = self.offset.y * pos.y;
|
||||
self.pos = self.correctBounds(self.pos);
|
||||
self.actualpos = self.pos;
|
||||
}
|
||||
|
||||
pub fn draw(self: *@This()) void {
|
||||
r.DrawTextureRec(
|
||||
self.tex,
|
||||
r.Rectangle{
|
||||
.x = @floor(@mod((self.actualpos.x + self.actualpos.y) / 4, self.frames)) * self.size.width,
|
||||
.y = self.dir.spritePosY(self.size.height),
|
||||
.width = self.size.width,
|
||||
.height = self.size.height,
|
||||
},
|
||||
r.Vector2{
|
||||
.x = self.actualpos.x + self.size.x,
|
||||
.y = self.actualpos.y + self.size.y,
|
||||
},
|
||||
r.WHITE,
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue