Object bounding boxes are defined in the tiled map editor, alongside navmeshes.

master
immibis 2025-02-18 23:11:20 +01:00
parent 6cf3dbd607
commit b7f31cdeaf
10 changed files with 141 additions and 157 deletions

View File

@ -58,7 +58,10 @@ sprite_%.o: $(PROJECT_ROOT)sprites/%.xcf
@# 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 $@)"
python3 "$(PROJECT_ROOT)compile_navmesh.py" "$<" "$@" "$(patsubst navmesh_%.c,%,$@)"
@# symbols are navmesh_% and predef_%
navmesh_%.o: $(PROJECT_ROOT)compiled_structures.h $(PROJECT_ROOT)objids.h
%.png: $(PROJECT_ROOT)sprites/%.xcf
gimp -in -b '(let ((image (car (gimp-xcf-load 0 "$<" "$(notdir $<)")))) ;\

View File

@ -14,16 +14,21 @@ def points_to_edges(points):
points = points + [points[0]]
return zip(points[:-1], points[1:])
for polygon in tree.findall("objectgroup[@name='navmesh']/object/polygon"):
basex = int(polygon.getparent().attrib["x"])
basey = int(polygon.getparent().attrib["y"])
def parse_object_polygon_points(object):
polygon_data = object.find("polygon")
basex = int(object.attrib["x"])
basey = int(object.attrib["y"])
if polygon_data is None:
# It's a rectangle by default
width = float(object.attrib["width"])
height = float(object.attrib["height"])
points = [(basex, basey), (basex + width, basey), (basex + width, basey + height), (basex, basey + height)]
else:
points = []
for point in polygon.attrib["points"].split(" "):
for point in polygon_data.attrib["points"].split(" "):
x,y=point.split(",")
x,y=int(x)+basex,int(y)+basey
points.append((x,y))
#print(points)
points.append((int(x)+basex, int(y)+basey))
# check winding order
ax,ay=points[0]
bx,by=points[1]
cx,cy=points[2]
@ -33,6 +38,23 @@ for polygon in tree.findall("objectgroup[@name='navmesh']/object/polygon"):
# positive for clockwise winding order. Example clockwise winding order: (0,0), (1,0), (0,1)
if cross < 0:
points = points[::-1]
return points
# Returns P,Q,R such that Px+Qy+R=0 for points on the edge, and >0 for points on the right (inside polygon for clockwise winding order)
def get_edge_equation(a, b):
(ax,ay),(bx,by)=a,b
# 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
return P,Q,R
for object in tree.findall("objectgroup[@name='navmesh']/object"):
points = parse_object_polygon_points(object)
poly_id = len(polys)
polys.append(points)
@ -69,23 +91,14 @@ for dest in range(len(polys)):
out = open(outpath,"w")
out.write("#include \"compiled_structures.h\"\n")
out.write("#include \"objids.h\"\n")
out.write(f"static const struct navmesh_tri {basename}_triangles[{len(polys)}] = {{\n");
for poly_id,points in enumerate(polys):
out.write("\t{\n")
out.write("\t\t"+str(len(points))+",\n")
out.write("\t\t(const struct navmesh_tri_edge[]){\n")
for a,b in points_to_edges(points):
(ax,ay),(bx,by) = a,b
# 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
P,Q,R = get_edge_equation(a,b)
edge_id = tuple(sorted((a,b)))
polys_on_edge = edge2poly[edge_id]
assert len(polys_on_edge) in (1,2)
@ -95,12 +108,10 @@ for poly_id,points in enumerate(polys):
else:
other_poly_id = -1
(ax,ay),(bx,by)=a,b
centx, centy = (ax+bx)/2, (ay+by)/2
out.write(f"\t\t\t{{{P},{Q},{R},{other_poly_id},{{{int(centx)},{int(centy)}}}}},\n")
out.write("\t\t},\n")
out.write("\t\t(const struct navmesh_point[]){" + ",".join(f"{{{x},{y}}}" for x,y in points) + "},\n")
out.write("\t},\n");
@ -112,4 +123,21 @@ for dest in range(len(polys)):
out.write(str(pathfind_matrix[source][dest])+",")
out.write("\n")
out.write("};\n")
out.write(f"extern const struct navmesh {basename} = {{{len(polys)}, {basename}_triangles, {basename}_pathfind}};\n")
out.write(f"extern const struct navmesh navmesh_{basename} = {{{len(polys)}, {basename}_triangles, {basename}_pathfind}};\n")
out.write("extern const struct level_predef_data predef_"+basename+" = {\n")
out.write("\t(const struct level_clickregion[]){\n") # clickregions start
for object in tree.findall("objectgroup[@name='clickable']/object"):
out.write("\t\t{\n");
points = parse_object_polygon_points(object)
out.write("\t\t\t"+object.attrib["name"]+",\n")
out.write("\t\t\t" + str(len(points)) + ",\n")
out.write("\t\t\t(const struct level_clickregion_edge[]){\n")
for a,b in points_to_edges(points):
P,Q,R = get_edge_equation(a,b)
out.write(f"\t\t\t\t{{{P},{Q},{R}}},\n")
out.write("\t\t\t},\n")
out.write("\t\t},\n");
out.write("\t\t{0}\n")
out.write("\t},\n") # clickregions end
out.write("};\n")

View File

@ -6,8 +6,8 @@ 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
float a,b,c; // line equation a*x + b*y + c > 0 if point is in polygon, and value gives distance from line.
int other_tri; // -1 if this edge is a border of navmesh, else polygon it connects to
struct navmesh_point center; // intermediate point for use in routing. could be improved later.
};
struct navmesh_tri {
@ -25,5 +25,17 @@ struct navmesh {
const unsigned char *pathfindgrid; // [dest*num_tris + source] gives index of next tri. Placeholder value is used when dest==source.
};
struct level_clickregion_edge {
float a,b,c; // line equation a*x + b*y + c > 0 if point is in polygon
};
struct level_clickregion {
int id;
int num_edges;
const struct level_clickregion_edge *edges;
};
struct level_predef_data {
const struct level_clickregion *clickregions; // terminated by null entry
};
#endif /* COMPILED_STRUCTURES_H_ */

View File

@ -9,6 +9,7 @@ struct object {
int width;
int height;
const char *pixels;
const struct level_clickregion *clickregion; // if not null, overrides click bounds check
};
typedef void (*script_wake_fn)(struct script *scr, int wakeupMode, int arg1, int arg2, int arg3, int arg4);
@ -54,7 +55,8 @@ void push_scene(int scene);
void replace_scene(int scene);
void pop_scene();
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);
struct object *scene_add_object(int id, int x, int y, int width, int height, const char *pixels);
void scene_load_predef(const struct level_predef_data *predef);
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);
@ -67,10 +69,6 @@ void start_player_walk_to_point(int x, int y); // x and y are not corrected to l
#define SCENE_MANAGERS_OFFICE_SAFE 3
#define SCENE_BASEMENT 4
// 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, int fromscene);
void onclick(int curscene, int objid);

View File

@ -1,6 +1,7 @@
#include <assert.h>
#include <stdio.h>
#include "engine.h"
#include "objids.h"
#define BGWIDTH 1280
#define BGHEIGHT 800
@ -15,53 +16,41 @@ extern const char _binary_sprite_stickman_raw_start[];
extern struct navmesh navmesh_lobby;
extern struct navmesh navmesh_managers_office;
extern struct navmesh navmesh_basement;
extern struct level_predef_data predef_basement, predef_lobby, predef_managers_office;
#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
#define OBJID_PLAYER_WALK_TO_DOOR_SCRIPT 6
#define OBJID_MANAGERS_OFFICE_SAFE 7
#define OBJID_MANAGERS_OFFICE_TRAPDOOR 8
#define OBJID_BASEMENT_POPCORN 9
#define OBJID_BASEMENT_ELECTRICAL_BOX 10
#define OBJID_BASEMENT_LADDER 11
#define OBJID_CLOSE_MODAL 12
static void create_player(int x, int y) {
const int WIDTH=51, HEIGHT=111;
scene_add_object(OBJID_PLAYER, x-WIDTH/2, y-HEIGHT, WIDTH, HEIGHT, _binary_sprite_stickman_raw_start);
}
void scene_setup(int scene, int fromscene) {
top_scene.render_fn = standard_scene_render;
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_load_predef(&predef_lobby);
switch(fromscene) {
case SCENE_MANAGERS_OFFICE:
scene_add_object(OBJID_PLAYER, 308-51/2, 445-111, 51, 111, _binary_sprite_stickman_raw_start);
create_player(308, 445);
break;
default:
scene_add_object(OBJID_PLAYER, 424, 575, 51, 111, _binary_sprite_stickman_raw_start);
create_player(424, 675);
break;
}
top_scene.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, 740, 440, 151, 276, nullptr);
scene_add_object(OBJID_MANAGERS_OFFICE_TRAPDOOR, 332, 565, 139, 87, nullptr);
scene_add_object(OBJID_MANAGERS_OFFICE_SAFE, 784, 297, 191, 188, nullptr);
scene_load_predef(&predef_managers_office);
switch(fromscene) {
case SCENE_LOBBY:
scene_add_object(OBJID_PLAYER, 804-51/2, 708-111, 51, 111, _binary_sprite_stickman_raw_start);
create_player(804, 708);
break;
case SCENE_BASEMENT:
scene_add_object(OBJID_PLAYER, 408-51/2, 559-111, 51, 111, _binary_sprite_stickman_raw_start);
break;
case SCENE_MANAGERS_OFFICE_SAFE:
scene_add_object(OBJID_PLAYER, 895-51/2, 497-111, 51, 111, _binary_sprite_stickman_raw_start);
create_player(408, 559);
break;
default:
scene_add_object(OBJID_PLAYER, 424, 575, 51, 111, _binary_sprite_stickman_raw_start);
create_player(424, 675);
break;
}
top_scene.navmesh = &navmesh_managers_office;
@ -73,13 +62,13 @@ void scene_setup(int scene, int fromscene) {
break;
case SCENE_BASEMENT:
scene_add_object(OBJID_BACKGROUND, 0, 0, BGWIDTH, BGHEIGHT, _binary_sprite_basement_raw_start);
scene_add_object(OBJID_BASEMENT_LADDER, 396, 86, 85, 312, nullptr);
scene_load_predef(&predef_basement);
switch(fromscene) {
case SCENE_MANAGERS_OFFICE:
scene_add_object(OBJID_PLAYER, 435-51/2, 404-111, 51, 111, _binary_sprite_stickman_raw_start);
create_player(435, 404);
break;
default:
scene_add_object(OBJID_PLAYER, 424, 575, 51, 111, _binary_sprite_stickman_raw_start);
create_player(424, 675);
break;
}
top_scene.navmesh = &navmesh_basement;

View File

@ -1,34 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.9" tiledversion="1.9.2" orientation="orthogonal" renderorder="right-down" width="40" height="25" tilewidth="32" tileheight="32" infinite="0" nextlayerid="5" nextobjectid="4">
<layer id="1" name="Tile Layer 1" width="40" height="25">
<data encoding="csv">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data>
</layer>
<imagelayer id="2" name="Image Layer 1">
<image source="../build/default/basement.png" width="1280" height="800"/>
</imagelayer>
@ -40,7 +11,7 @@
<polygon points="0,0 -101,174 821,174 679,-74 248,-76"/>
</object>
</objectgroup>
<objectgroup id="4" name="Object Layer 2">
<object id="3" x="396" y="86" width="85" height="312"/>
<objectgroup id="4" name="clickable">
<object id="3" name="OBJID_BASEMENT_LADDER" x="396" y="86" width="85" height="312"/>
</objectgroup>
</map>

View File

@ -1,40 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.9" tiledversion="1.9.2" orientation="orthogonal" renderorder="right-down" width="40" height="25" tilewidth="32" tileheight="32" infinite="0" nextlayerid="5" nextobjectid="19">
<layer id="1" name="Tile Layer 1" width="40" height="25">
<data encoding="csv">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data>
</layer>
<imagelayer id="2" name="Image Layer 1">
<image source="../build/default/lobby.png" width="1280" height="800"/>
</imagelayer>
<objectgroup id="3" name="Object Layer 1">
<object id="1" x="273" y="313" width="76" height="128"/>
<object id="3" x="908" y="322" width="53" height="117"/>
<objectgroup id="3" name="clickable">
<object id="1" name="OBJID_DOOR_TO_MANAGERS_OFFICE_FROM_LOBBY" x="273" y="313" width="76" height="128"/>
</objectgroup>
<objectgroup id="4" name="navmesh">
<object id="10" x="213" y="441">

View File

@ -1,29 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.9" tiledversion="1.9.2" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="32" tileheight="32" infinite="0" nextlayerid="5" nextobjectid="22">
<layer id="1" name="Tile Layer 1" width="30" height="20">
<data encoding="csv">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data>
</layer>
<map version="1.9" tiledversion="1.9.2" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="32" tileheight="32" infinite="0" nextlayerid="5" nextobjectid="24">
<imagelayer id="2" name="Image Layer 1">
<image source="../build/default/managers_office.png" width="1280" height="800"/>
</imagelayer>
@ -62,9 +38,13 @@
<polygon points="0,0 0,60 135,60"/>
</object>
</objectgroup>
<objectgroup id="4" name="Object Layer 2">
<object id="19" x="740" y="440" width="151" height="276"/>
<object id="20" x="332" y="565" width="139" height="87"/>
<object id="21" x="784" y="297" width="191" height="188"/>
<objectgroup id="4" name="clickable">
<object id="19" name="OBJID_DOOR_TO_LOBBY_FROM_MANAGERS_OFFICE" x="740" y="440" width="151" height="276"/>
<object id="22" name="OBJID_MANAGERS_OFFICE_TRAPDOOR" x="347" y="563">
<polygon points="0,0 -37,91 113,91 127,-1"/>
</object>
<object id="23" name="OBJID_MANAGERS_OFFICE_SAFE" x="784" y="417">
<polygon points="0,0 1,-122 150,-122 193,-79 192,75 40,75"/>
</object>
</objectgroup>
</map>

12
objids.h Normal file
View File

@ -0,0 +1,12 @@
#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 OBJID_PLAYER_WALK_SCRIPT 5
#define OBJID_PLAYER_WALK_TO_DOOR_SCRIPT 6
#define OBJID_MANAGERS_OFFICE_SAFE 7
#define OBJID_MANAGERS_OFFICE_TRAPDOOR 8
#define OBJID_BASEMENT_POPCORN 9
#define OBJID_BASEMENT_ELECTRICAL_BOX 10
#define OBJID_BASEMENT_LADDER 11
#define OBJID_CLOSE_MODAL 12

View File

@ -18,6 +18,7 @@
#include <inttypes.h>
#include "engine.h"
#include "compiled_structures.h"
#include "objids.h"
#define PLAYFIELD_WIDTH 1280
#define PLAYFIELD_HEIGHT 800
@ -182,7 +183,7 @@ void pop_scene() {
// any cleanup?
}
void scene_add_object(int id, int x, int y, int width, int height, const char *pixels) {
struct object *scene_add_object(int id, int x, int y, int width, int height, const char *pixels) {
if(id == 0) error(1, 0, "scene_add_object: id 0 is invalid");
struct object *objects = top_scene.objects;
@ -194,12 +195,19 @@ void scene_add_object(int id, int x, int y, int width, int height, const char *p
objects[i].width = width;
objects[i].height = height;
objects[i].pixels = pixels;
return;
return &objects[i];
}
}
error(1, 0, "too many game objects");
}
void scene_load_predef(const struct level_predef_data *predef) {
for(const struct level_clickregion *cr = predef->clickregions; cr->num_edges; cr++) {
struct object *obj = scene_add_object(cr->id, 0, 0, 0, 0, nullptr);
obj->clickregion = cr;
}
}
struct object *find_object_by_id(int id) {
struct object *objects = top_scene.objects;
for(int i = 0; i < MAX_OBJECTS_PER_SCENE; i++) {
@ -360,10 +368,23 @@ void handle_tap(int x, int y) {
struct navmesh *cur_navmesh = top_scene.navmesh;
struct object *objects = top_scene.objects;
for(int i = 0; i < MAX_OBJECTS_PER_SCENE; i++) {
if(objects[i].id && (unsigned int)(x - objects[i].x) < objects[i].width && (unsigned int)(y - objects[i].y) < objects[i].height) {
if(!objects[i].id) continue;
if(objects[i].clickregion) {
const struct level_clickregion *cr = objects[i].clickregion;
for(int i = 0; i < cr->num_edges; i++) {
if(cr->edges[i].a*x + cr->edges[i].b*y + cr->edges[i].c < 0)
goto click_not_on_object;
}
} else {
if((unsigned int)(x - objects[i].x) >= objects[i].width || (unsigned int)(y - objects[i].y) >= objects[i].height)
goto click_not_on_object;
}
// clicked on the object
onclick(sceneid, objects[i].id);
if(top_scene.id != sceneid) return; // early exit if scene changed, and don't walk the player either
}
click_not_on_object:;
}
if(walkgen == player_walk_generation) { // no other handler moved the player