diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Makefile b/Makefile
index 36e1a28..5e6ce20 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,9 @@ PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
OBJS = pinetab2_framework.o game.o
SPRITES = $(patsubst $(PROJECT_ROOT)sprites/%.xcf,%,$(wildcard $(PROJECT_ROOT)sprites/*.xcf))
+NAVMESHES = $(patsubst $(PROJECT_ROOT)navmesh/%.tmx,%,$(wildcard $(PROJECT_ROOT)navmesh/*.tmx))
+BUILD_MODE := debug
ifeq ($(BUILD_MODE),debug)
CFLAGS += -g
else ifeq ($(BUILD_MODE),run)
@@ -16,12 +18,13 @@ else
$(error Build mode $(BUILD_MODE) not supported by this Makefile)
endif
-CXX := aarch64-linux-gnu-$(CXX)
-CC := aarch64-linux-gnu-$(CC)
+CXX := aarch64-linux-gnu-g++
+CC := aarch64-linux-gnu-gcc
OBJCOPY := aarch64-linux-gnu-objcopy -O elf64-littleaarch64
CFLAGS += -I/usr/include
OBJS += $(patsubst %,sprite_%.o,$(SPRITES))
+OBJS += $(patsubst %,navmesh_%.o,$(NAVMESHES))
all: pinetab2_framework $(patsubst %,%.png,$(SPRITES))
@@ -29,18 +32,21 @@ pinetab2_framework: $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $^
$(EXTRA_CMDS)
-%.o: $(PROJECT_ROOT)%.cpp
+%.o: $(PROJECT_ROOT)%.cpp
$(CXX) -c $(CFLAGS) $(CXXFLAGS) $(CPPFLAGS) -o $@ $<
-%.o: $(PROJECT_ROOT)%.c
+%.o: $(PROJECT_ROOT)%.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
+%.o: %.c
+ $(CXX) -c $(CFLAGS) $(CXXFLAGS) $(CPPFLAGS) -o $@ $< -iquote "$(PROJECT_ROOT)"
+
clean:
rm -fr pinetab2_framework $(OBJS) $(EXTRA_CLEAN)
sprite_%.o: $(PROJECT_ROOT)sprites/%.xcf
gimp -in -b '(let ((image (car (gimp-xcf-load 0 "$<" "$(notdir $<)")))) ;\
- (gimp-image-scale image 1280 800) ;\
+ ;(gimp-image-scale image 1280 800) ;\
(gimp-image-rotate image ROTATE-90) ;\
(let ((layer (car (gimp-image-merge-visible-layers image CLIP-TO-IMAGE)))) ;\
(plug-in-colors-channel-mixer RUN-NONINTERACTIVE image layer 0 0 0 1 0 1 0 1 0 0) ; swap red and blue channels \
@@ -51,9 +57,12 @@ sprite_%.o: $(PROJECT_ROOT)sprites/%.xcf
rm $(patsubst %.o,%.raw,$@)
@# symbols are _binary_sprite_%_raw_start / _end / _size
+navmesh_%.c: $(PROJECT_ROOT)navmesh/%.tmx $(PROJECT_ROOT)compile_navmesh.py
+ python3 "$(PROJECT_ROOT)compile_navmesh.py" "$<" "$@" "$(basename $@)"
+
%.png: $(PROJECT_ROOT)sprites/%.xcf
gimp -in -b '(let ((image (car (gimp-xcf-load 0 "$<" "$(notdir $<)")))) ;\
- (gimp-image-scale image 1280 800) ;\
+ ;(gimp-image-scale image 1280 800) ;\
(let ((layer (car (gimp-image-merge-visible-layers image CLIP-TO-IMAGE)))) ;\
(file-png-save 1 image layer "$@" "$@" 0 9 0 0 0 0 0) ;\
) ;\
diff --git a/compile_navmesh.py b/compile_navmesh.py
new file mode 100644
index 0000000..2091722
--- /dev/null
+++ b/compile_navmesh.py
@@ -0,0 +1,110 @@
+#!/usr/bin/python3
+import lxml.etree, math, sys
+
+_, inpath, outpath, basename = sys.argv
+
+tree = lxml.etree.parse(inpath)
+
+tris = []
+edge2tri = {}
+
+for polygon in tree.findall("objectgroup[@name='navmesh']/object/polygon"):
+ basex = int(polygon.getparent().attrib["x"])
+ basey = int(polygon.getparent().attrib["y"])
+ points=[]
+ for point in polygon.attrib["points"].split(" "):
+ x,y=point.split(",")
+ x,y=int(x)+basex,int(y)+basey
+ points.append((x,y))
+ #print(points)
+
+ ax,ay=points[0]
+ bx,by=points[1]
+ cx,cy=points[2]
+ abx,aby=bx-ax,by-ay
+ acx,acy=cx-ax,cy-ay
+ cross = abx*acy-acx*aby
+ # positive for clockwise winding order. Example clockwise winding order: (0,0), (1,0), (0,1)
+ if cross < 0:
+ points = points[0],points[2],points[1]
+ bx,by,cx,cy=cx,cy,bx,by
+ cross = -cross
+
+ assert len(points)==3, "triangles only"
+ tri_id = len(tris)
+ tris.append(points)
+
+ for edge in [(points[0],points[1]), (points[1],points[2]), (points[2],points[0])]:
+ edge_id = tuple(sorted(edge))
+ if edge_id not in edge2tri: edge2tri[edge_id] = []
+ edge2tri[edge_id].append(tri_id)
+ assert len(edge2tri[edge_id]) <= 2, "more than 2 triangles share edge "+str(edge_id)
+
+pathfind_edges = [[] for _ in range(len(tris))]
+for edgetris in edge2tri.values():
+ assert len(edgetris) in (1,2)
+ if len(edgetris) == 1: continue
+ t1,t2 = edgetris
+ pathfind_edges[t1].append(t2)
+ pathfind_edges[t2].append(t1)
+
+pathfind_matrix = [[255 for _1 in range(len(tris))] for _2 in range(len(tris))]
+# pathfind_matrix[source][dest] is how to get to dest from source
+
+for dest in range(len(tris)):
+ # Find paths to tri by breadth-first search. We don't take the size of the triangle into account yet.
+ openset = [dest]
+ while len(openset)>0:
+ cur, openset = openset[0], openset[1:]
+ for adjacent in pathfind_edges[cur]:
+ if adjacent != dest and pathfind_matrix[adjacent][dest] == 255:
+ pathfind_matrix[adjacent][dest] = cur
+ openset.append(adjacent)
+
+
+
+out = open(outpath,"w")
+
+out.write("#include \"compiled_structures.h\"\n")
+out.write(f"static const struct navmesh_tri {basename}_triangles[{len(tris)}] = {{\n");
+for tri_id,points in enumerate(tris):
+ out.write("\t{\n\t\t{\n");
+ for a,b,c in [(points[0],points[1],points[2]), (points[1],points[2],points[0]), (points[2],points[0],points[1])]:
+ (ax,ay),(bx,by),(cx,cy) = a,b,c
+ # edge a-b and c is the other point
+ # Line equation Px+Qy+R=0 from https://math.stackexchange.com/questions/422602/convert-two-points-to-line-eq-ax-by-c-0
+ P,Q,R=ay-by,bx-ax,ax*by-bx*ay
+ # Line equation >0 for points inside the triangle.
+ # Normalize so that P^2+Q^2=1 so that the equation result tells us the distance. This can be used to find the closest triangle to a point.
+ norm=1/math.sqrt(P*P+Q*Q)
+ P *= norm
+ Q *= norm
+ R *= norm
+
+ edge_id = tuple(sorted((a,b)))
+ tris_on_edge = edge2tri[edge_id]
+ assert len(tris_on_edge) in (1,2)
+ assert tri_id in tris_on_edge
+ if len(tris_on_edge) == 2:
+ other_tri_id = [x for x in tris_on_edge if x!=tri_id][0]
+ else:
+ other_tri_id = -1
+
+ centx, centy = (ax+bx)/2, (ay+by)/2
+
+ out.write(f"\t\t\t{{{P},{Q},{R},{other_tri_id},{{{int(centx)},{int(centy)}}}}},\n")
+
+
+
+ out.write("\t\t},\n")
+ out.write("\t\t{" + ",".join(f"{{{x},{y}}}" for x,y in points) + "},\n")
+ out.write("\t},\n");
+out.write("};\n")
+out.write(f"static const unsigned char {basename}_pathfind[{len(tris)*len(tris)}] = {{\n")
+for dest in range(len(tris)):
+ out.write("\t")
+ for source in range(len(tris)):
+ out.write(str(pathfind_matrix[source][dest])+",")
+ out.write("\n")
+out.write("};\n")
+out.write(f"extern const struct navmesh {basename} = {{{len(tris)}, {basename}_triangles, {basename}_pathfind}};\n")
diff --git a/compiled_structures.h b/compiled_structures.h
new file mode 100644
index 0000000..d772d2c
--- /dev/null
+++ b/compiled_structures.h
@@ -0,0 +1,27 @@
+#ifndef COMPILED_STRUCTURES_H_
+#define COMPILED_STRUCTURES_H_
+
+
+struct navmesh_point {
+ int x, y;
+};
+struct navmesh_tri_edge {
+ float a,b,c; // line equation a*x + b*y + c > 0 if point is in triangle, and value gives distance from line.
+ int other_tri; // -1 if this edge is a border of navmesh, else triangle it connects to
+ struct navmesh_point center; // intermediate point for use in routing. could be improved later.
+};
+struct navmesh_tri {
+ struct navmesh_tri_edge edges[3];
+ struct navmesh_point points[3];
+ // edges[0]: points[0]-points[1]
+ // edges[1]: points[1]-points[2]
+ // edges[2]: points[2]-points[0]
+};
+struct navmesh {
+ int num_tris;
+ const struct navmesh_tri *tris;
+ const unsigned char *pathfindgrid; // [dest*num_tris + source] gives index of next tri. Placeholder value is used when dest==source.
+};
+
+
+#endif /* COMPILED_STRUCTURES_H_ */
diff --git a/engine.h b/engine.h
index aee7d51..20c9413 100644
--- a/engine.h
+++ b/engine.h
@@ -1,4 +1,4 @@
-#define MAX_GAME_OBJECTS 300
+#define MAX_GAME_OBJECTS 100
extern struct object {
int id; // 0 if slot unused
int x;
@@ -8,15 +8,51 @@ extern struct object {
const char *pixels;
} objects[MAX_GAME_OBJECTS];
+#define MAX_GAME_SCRIPTS 100
+typedef void (*script_wake_fn)(struct script *scr, int wakeupMode, int arg1, int arg2, int arg3, int arg4);
+#define SCRIPT_HEADER \
+ int id /* 0 if slot unused */; \
+ int wakeupMode; \
+ script_wake_fn wakeupFn; \
+ int wakeupArg1; /* for SCRIPT_WAKEUP_OTHER_SCRIPT */
+extern struct script {
+ SCRIPT_HEADER
+
+ int vars[100];
+
+} scripts[MAX_GAME_SCRIPTS];
+
+struct script_player_walk {
+ SCRIPT_HEADER
+ int targetX;
+ int targetY;
+ int targetNavmeshTri;
+ int currentNavmeshTri;
+};
+
+#define SCRIPT_WAKEUP_VIDEO_FRAME 1
+#define SCRIPT_WAKEUP_OTHER_SCRIPT 2
+#define SCRIPT_WAKEUP_OTHER_SCRIPT_INTERRUPTED 3 // Delivered to script that registers for SCRIPT_WAKEUP_OTHER_SCRIPT if that script is interrupted instead of completing.
+
+
// engine
void transition_scene(int scene); // Immediately stops all event processing for current scene, which is unloaded before this function returns
void scene_add_clickrect(int id, int x, int y, int width, int height);
void scene_add_object(int id, int x, int y, int width, int height, const char *pixels);
+void scene_set_navmesh(struct navmesh *navmesh);
+struct object *find_object_by_id(int id);
+struct script *scene_add_script(int id, bool interrupt_existing);
+struct script *scene_get_script(int id);
+bool deliver_script_wakeup(int wakeupMode, int wakeupArg1, int wakeupType, int arg1, int arg2, int arg3, int arg4); // deliver to scripts with given wakeupMode and wakeupArg1. Returns true if scene changed.
// game-specific constants
#define SCENE_LOBBY 1
#define SCENE_MANAGERS_OFFICE 2
+// The player ID and walk script are globally relevant
+#define OBJID_PLAYER 4
+#define OBJID_PLAYER_WALK_SCRIPT 5
+
// game.cpp
void scene_setup(int scene);
void onclick(int curscene, int objid);
diff --git a/game.cpp b/game.cpp
index bfd6d33..076b2c3 100644
--- a/game.cpp
+++ b/game.cpp
@@ -6,19 +6,28 @@
extern const char _binary_sprite_lobby_raw_start[];
extern const char _binary_sprite_managers_office_raw_start[];
+extern const char _binary_sprite_stickman_raw_start[];
+
+extern struct navmesh navmesh_lobby;
+
#define OBJID_BACKGROUND 1
#define OBJID_DOOR_TO_MANAGERS_OFFICE_FROM_LOBBY 2
#define OBJID_DOOR_TO_LOBBY_FROM_MANAGERS_OFFICE 3
+// #define OBJID_PLAYER 4
+// #define SCRIPTID_PLAYER_WALK 5
void scene_setup(int scene) {
switch(scene) {
case SCENE_LOBBY:
scene_add_object(OBJID_BACKGROUND, 0, 0, BGWIDTH, BGHEIGHT, _binary_sprite_lobby_raw_start);
scene_add_object(OBJID_DOOR_TO_MANAGERS_OFFICE_FROM_LOBBY, 273, 313, 76, 128, nullptr);
+ scene_add_object(OBJID_PLAYER, 424, 575, 51, 111, _binary_sprite_stickman_raw_start);
+ scene_set_navmesh(&navmesh_lobby);
break;
case SCENE_MANAGERS_OFFICE:
scene_add_object(OBJID_BACKGROUND, 0, 0, BGWIDTH, BGHEIGHT, _binary_sprite_managers_office_raw_start);
scene_add_object(OBJID_DOOR_TO_LOBBY_FROM_MANAGERS_OFFICE, 273, 313, 76, 128, nullptr);
+ scene_add_object(OBJID_PLAYER, 424, 575, 51, 111, _binary_sprite_stickman_raw_start);
break;
}
}
@@ -41,3 +50,4 @@ void onclick(int curscene, int objid) {
break;
}
}
+
diff --git a/navmesh/lobby.tmx b/navmesh/lobby.tmx
new file mode 100644
index 0000000..a1682c4
--- /dev/null
+++ b/navmesh/lobby.tmx
@@ -0,0 +1,68 @@
+
+
diff --git a/pinetab2_framework.cpp b/pinetab2_framework.cpp
index 472f081..ce369e8 100644
--- a/pinetab2_framework.cpp
+++ b/pinetab2_framework.cpp
@@ -17,14 +17,22 @@
#include
#include
#include "engine.h"
+#include "compiled_structures.h"
#define PLAYFIELD_WIDTH 1280
#define PLAYFIELD_HEIGHT 800
-
static int touchscreen_fd;
+static struct navmesh null_navmesh = {0};
+struct navmesh *cur_navmesh = &null_navmesh;
+struct object objects[MAX_GAME_OBJECTS];
+struct script scripts[MAX_GAME_SCRIPTS];
+bool need_rerender = false;
+
+
+
#define MAX_MT_SLOTS 16
static struct mtslot {
int x, y, touch, lasttouch;
@@ -131,7 +139,8 @@ struct sprite {
static uint32_t *curfb; // used during render cycle
static void blit(int x, int y, const struct sprite *spr) {
uint32_t *inbase = spr->pixels;
- printf("blit %dx%d at %d,%d\n", spr->width, spr->height, x, y);
+ //printf("blit %dx%d at %d,%d\n", spr->width, spr->height, x, y);
+ y = PLAYFIELD_HEIGHT - spr->height - y;
if(y >= PLAYFIELD_HEIGHT || x >= PLAYFIELD_WIDTH || y <= -spr->height || x <= -spr->width) return;
if(spr->height > PLAYFIELD_HEIGHT) error(1, 0, "sprite bigger than playfield not supported"); // would need to clip top and bottom simultaneously - not implemented for now
@@ -153,8 +162,9 @@ static void blit(int x, int y, const struct sprite *spr) {
}
}
-struct object objects[MAX_GAME_OBJECTS];
-bool need_rerender = false;
+void scene_set_navmesh(struct navmesh *navmesh) {
+ cur_navmesh = navmesh;
+}
static uint32_t scene_generation_count = 0;
static int curscene;
@@ -162,6 +172,8 @@ void transition_scene(int scene) {
scene_generation_count++;
curscene = scene;
memset(objects, 0, sizeof objects);
+ memset(scripts, 0, sizeof scripts); // script state is local to scene, so no cancel notification
+ cur_navmesh = &null_navmesh;
scene_setup(scene);
}
@@ -182,6 +194,106 @@ void scene_add_object(int id, int x, int y, int width, int height, const char *p
error(1, 0, "too many game objects");
}
+struct object *find_object_by_id(int id) {
+ for(int i = 0; i < MAX_GAME_OBJECTS; i++) {
+ if(objects[i].id == id)
+ return &objects[i];
+ }
+ return nullptr;
+}
+struct script *scene_add_script(int id, bool interrupt_existing) {
+ if(interrupt_existing && id != OBJID_PLAYER_WALK_SCRIPT) error(1, 0, "scene_add_script: interrupt not implemented");
+ for(int i = 0; i < MAX_GAME_SCRIPTS; i++) {
+ if(scripts[i].id == id && interrupt_existing) {
+ // interrupt existing script?
+ // player walk script is safe to trivially cancel, at least
+ scripts[i].id = 0;
+ }
+ if(scripts[i].id == 0) {
+ memset(&scripts[i], 0, sizeof(scripts[i]));
+ scripts[i].id = id;
+ return &scripts[i];
+ }
+ }
+ error(1, 0, "too many game scripts");
+ __builtin_unreachable();
+}
+
+static void closest_point_inside_tri(int *x, int *y, const struct navmesh_tri *tri) {
+ double fx = *x, fy = *y;
+ int edgeHitMask = 0;
+
+ // Moving a point outside of one half-plane can move it into another half-plane, so check again.
+ // If this happens, we must be in a corner region.
+ // Repeated normal-vector moves don't necessarily bring us to the corner, but we can just hardcode the corner.
+ for(int j = 0; j < 2; j++) {
+ for(int i = 0; i < 3; i++) {
+ double edgeval = fx*tri->edges[i].a + fy*tri->edges[i].b + tri->edges[i].c;
+ if(edgeval <= 0) {
+ // edge.a,b is normalized so that moving 1 unit of a and 1 unit of b changes edgeval by 1.
+ fx -= tri->edges[i].a*edgeval;
+ fy -= tri->edges[i].b*edgeval;
+ edgeHitMask |= 1 << i;
+ }
+ }
+ }
+
+ switch(edgeHitMask) {
+ case 3: // hit edge 0-1 and 1-2
+ *x = tri->points[1].x;
+ *y = tri->points[1].y;
+ return;
+ case 5: // hit edge 0-1 and 2-0
+ *x = tri->points[0].x;
+ *y = tri->points[0].y;
+ return;
+ case 6: // hit edge 1-2 and 2-0
+ *x = tri->points[2].x;
+ *y = tri->points[2].y;
+ return;
+ }
+
+ // may be slightly outside of triangle due to rounding errors, but good enough for us
+ *x = (int)(fx + 0.5);
+ *y = (int)(fy + 0.5);
+}
+
+static double point_to_point_dist(int x, int y, const struct navmesh_point *pt) {
+ x -= pt->x;
+ y -= pt->y;
+ return sqrt(x*x + y*y);
+}
+
+// Returns distance from triangle, 0 if inside.
+static double is_point_in_tri(int x, int y, const struct navmesh_tri *tri) {
+ double edgedist[3]; // positive if outside
+ for(int edge = 0; edge < 3; edge++)
+ edgedist[edge] = -(x*tri->edges[edge].a + y*tri->edges[edge].b + tri->edges[edge].c);
+ if(edgedist[0] < 0 && edgedist[1] < 0 && edgedist[2] < 0) return 0; // inside
+ if(edgedist[0] >= 0 && edgedist[1] >= 0) return point_to_point_dist(x, y, &tri->points[0]);
+ if(edgedist[1] >= 0 && edgedist[2] >= 0) return point_to_point_dist(x, y, &tri->points[1]);
+ if(edgedist[2] >= 0 && edgedist[0] >= 0) return point_to_point_dist(x, y, &tri->points[2]);
+ if(edgedist[0] >= 0) return edgedist[0];
+ if(edgedist[1] >= 0) return edgedist[1];
+ if(edgedist[2] >= 0) return edgedist[2];
+ return 0; // branch should be unreachable; maybe within epsilon of triangle border?
+}
+static int find_navmesh_tri(int x, int y, int tolerance) {
+ int best_tri = -1;
+ double best_dist = tolerance;
+ for(int i = 0; i < cur_navmesh->num_tris; i++) {
+ double dist = is_point_in_tri(x, y, &cur_navmesh->tris[i]);
+ if(dist == 0)
+ return i;
+ if(dist < best_dist) {
+ best_dist = dist;
+ best_tri = i;
+ }
+ }
+ return best_tri;
+}
+
+static void update_player_walk_script_on_frame(struct script *scr, int wakeupMode, int, int, int, int);
void handle_tap(int x, int y) {
uint32_t gencount = scene_generation_count;
for(int i = 0; i < MAX_GAME_OBJECTS; i++) {
@@ -190,8 +302,108 @@ void handle_tap(int x, int y) {
onclick(curscene, objects[i].id);
}
}
+
+ struct script_player_walk *player_walk_script = (struct script_player_walk *)scene_add_script(OBJID_PLAYER_WALK_SCRIPT, true);
+
+ struct object *player = find_object_by_id(OBJID_PLAYER);
+ if(player) {
+ int destTri = find_navmesh_tri(x, y, 50);
+ if(destTri != -1) {
+ int targetX = x, targetY = y;
+ closest_point_inside_tri(&targetX, &targetY, &cur_navmesh->tris[destTri]);
+
+ player_walk_script->targetX = targetX;
+ player_walk_script->targetY = targetY;
+ player_walk_script->targetNavmeshTri = destTri;
+ player_walk_script->wakeupMode = SCRIPT_WAKEUP_VIDEO_FRAME;
+ player_walk_script->wakeupFn = update_player_walk_script_on_frame;
+
+ int sourceTri = find_navmesh_tri(player->x+player->width*2, player->y+player->height, 1000000);
+ player_walk_script->currentNavmeshTri = sourceTri;
+ printf("set course from triangle %d to %d\n", sourceTri, destTri);
+ } else {
+ player_walk_script->id = 0;
+ }
+ } else {
+ player_walk_script->id = 0;
+ }
}
+static int move_towards(int cur, int target, int maxrate) {
+ if(target > cur+maxrate) return cur+maxrate;
+ if(target < cur-maxrate) return cur-maxrate;
+ return target;
+}
+
+static void update_player_walk_script_on_frame(struct script *scr, int wakeupMode, int, int, int, int) {
+ struct script_player_walk *player_walk_script = (struct script_player_walk *)scr;
+ if(!player_walk_script) return;
+ struct object *player = find_object_by_id(OBJID_PLAYER);
+ if(!player) {
+ player_walk_script->id = 0;
+ return;
+ }
+
+ if(player_walk_script->currentNavmeshTri == -1) {
+ // something weird happened. just teleport player and end movement.
+ player->x = player_walk_script->targetX - player->width/2;
+ player->y = player_walk_script->targetY - player->height;
+ player_walk_script->id = 0;
+ need_rerender = true;
+ } else {
+ int x = player->x+player->width/2;
+ int y = player->y+player->height;
+ int nmtri = player_walk_script->currentNavmeshTri;
+ int nextX;
+ int nextY;
+ int nextTri = -1;
+ if(nmtri == player_walk_script->targetNavmeshTri) {
+ nextX = player_walk_script->targetX;
+ nextY = player_walk_script->targetY;
+ } else {
+ nextTri = cur_navmesh->pathfindgrid[player_walk_script->targetNavmeshTri * cur_navmesh->num_tris + nmtri];
+ nextX = -1;
+ for(int edge = 0; edge < 3; edge++) {
+ if(cur_navmesh->tris[nmtri].edges[edge].other_tri == nextTri) {
+ nextX = cur_navmesh->tris[nmtri].edges[edge].center.x;
+ nextY = cur_navmesh->tris[nmtri].edges[edge].center.y;
+ goto center_found;
+ }
+ }
+ // shouldn't happen
+ player_walk_script->id = 0;
+ return;
+ center_found:;
+ }
+ if(nextX == x && nextY == y) {
+ printf("in triangle %d, next triangle %d\n", nmtri, nextTri);
+ if(x == player_walk_script->targetX && y == player_walk_script->targetY) {
+ player_walk_script->id = 0;
+ } else {
+ player_walk_script->currentNavmeshTri = nextTri; // even if -1
+ }
+ } else {
+ x = move_towards(x, nextX, 5);
+ y = move_towards(y, nextY, 2);
+ player->x = x - player->width/2;
+ player->y = y - player->height;
+ }
+ need_rerender = true;
+ }
+}
+
+bool deliver_script_wakeup(int wakeupMode, int wakeupArg1, int wakeupType, int arg1, int arg2, int arg3, int arg4) {
+ uint32_t gen = scene_generation_count;
+ for(int i = 0; i < MAX_GAME_SCRIPTS; i++) {
+ if(scripts[i].id && scripts[i].wakeupMode == wakeupMode && scripts[i].wakeupArg1 == wakeupArg1) {
+ scripts[i].wakeupFn(&scripts[i], wakeupType, arg1, arg2, arg3, arg4);
+ if(scene_generation_count != gen) return true; // scene changed - abort early
+ }
+ }
+ return false;
+}
+
+
int main(int argc, char **argv) {
if(getuid() != 0) {
@@ -296,7 +508,7 @@ int main(int argc, char **argv) {
uint64_t dummy; // don't care about expiration count
if(read(timerfd, &dummy, sizeof dummy) < 0) error(1, errno, "read (timerfd)");
current_animframe++;
- printf("animation frame %u\n", (unsigned int)current_animframe);
+ //printf("animation frame %u\n", (unsigned int)current_animframe);
need_rerender = true;
}
if(polls[2].revents & POLLIN) {
@@ -319,20 +531,18 @@ int main(int argc, char **argv) {
}
}
+ if(!awaiting_vblank) {
+ // return value ignored
+ // What is this really doing? It's giving scripts the opportunity to trigger a video frame.
+ // But if no script actually does, this can run over and over many times in the same video frame.
+ // It's not saying a video frame happened, it's saying one can happen.
+ deliver_script_wakeup(SCRIPT_WAKEUP_VIDEO_FRAME, 0, SCRIPT_WAKEUP_VIDEO_FRAME, 0, 0, 0, 0);
+ }
+
if(need_rerender && !awaiting_vblank) {
cur_buffer = 1-cur_buffer; // for testing, just keep writing into the same buffer
curfb = fbs[cur_buffer];
-
- //printf("%d %d\n", sizeof basement_pixels, 1280*800*4);
- /*struct sprite bgsprite = {
- .width = 1280,
- .height = 800,
- .pixels = (uint32_t*)_binary_sprite_lobby_raw_start
- };
- memset(curfb, 0, 1280*800*4);
- blit(mtslots[0].y-640, mtslots[0].x-400, &bgsprite);*/
-
for(int i = 0; i < MAX_GAME_OBJECTS; i++) {
if(objects[i].id != 0 && objects[i].pixels) {
struct sprite spr = {.width = objects[i].width, .height = objects[i].height, .pixels = (uint32_t*)objects[i].pixels};
diff --git a/sprites/lobby.xcf b/sprites/lobby.xcf
index 5eb4c52..b22aded 100644
Binary files a/sprites/lobby.xcf and b/sprites/lobby.xcf differ
diff --git a/sprites/managers_office.xcf b/sprites/managers_office.xcf
index f668ddd..10d09ac 100644
Binary files a/sprites/managers_office.xcf and b/sprites/managers_office.xcf differ
diff --git a/sprites/stickman.xcf b/sprites/stickman.xcf
new file mode 100644
index 0000000..eb961ed
Binary files /dev/null and b/sprites/stickman.xcf differ