PACKAGE = coco-planes
VERSION = 0.1.0
COPYRIGHT_YEARS = 2026

DISPLAY_NAME = COCO PLANES
PROG = coco-planes
PROGRAM_ALL_CAPS_8_CHAR = CCPLANES

TARGET = coco

# Directories of required libraries (typically symlinks).
# These libraries are available on the CMOC home page.
#
DECBFILE_LIB_DIR = decbfile
CARDGAME_LIB_DIR = cardgame
BCONTROL_LIB_DIR = bcontrol

CMOC = cmoc
PERL = perl
TAR = tar
TOUCH = touch
GREP = grep
WRITECOCOFILE = writecocofile
INSTALL_LOADER = install-coco-boot-loader

NDEBUG = -DNDEBUG
WERROR = -Werror
INTERMEDIATE = #-i
OPTIM = -fomit-frame-pointer #-O0
WARNINGS = -Wno-inline-asm-array-indexes -Wno-unknown-first-dim -Wno-too-many-elements

# Should be 1 to let the user type in an RNG seed, 0 otherwise.
SEED_FROM_USER = 0

DEFINES = \
	-DVERSION=\"$(VERSION)\" \
	-DCOPYRIGHT_YEARS=\"$(COPYRIGHT_YEARS)\" \
	-DDISPLAY_NAME='"$(DISPLAY_NAME)"' \
	-DAPP_ENTRY=0x$(APP_ENTRY) \
	-DSEED_FROM_USER=$(SEED_FROM_USER)

CFLAGS = --no-relocate $(NDEBUG) $(WERROR) $(WARNINGS) $(INTERMEDIATE) $(OPTIM) $(DEFINES) \
			$(DECBFILE_CFLAGS) $(CARDGAME_CFLAGS) $(BCONTROL_CFLAGS)
#CFLAGS += -Wno-uncalled-static

# See HACKING.md for a memory map.
#
APP_ENTRY = 4000
APP_MAX_END = FEEE
ORG = --org=$(APP_ENTRY)
LDFLAGS = $(INTERMEDIATE)

# Bitmap filenames must only use [a-z0-9_], plus a dot for the extension.
# The corresponding C struct name will be the filename with the dot
# replaced with an underscore, e.g., shooter_bm.

BIPLANE_BITMAPS = \
	biplane_red_00.bm \
	biplane_red_01.bm \
	biplane_red_02.bm \
	biplane_red_03.bm \
	biplane_red_04.bm \
	biplane_red_05.bm \
	biplane_red_06.bm \
	biplane_red_07.bm \
	biplane_red_08.bm \
	biplane_red_09.bm \
	biplane_red_10.bm \
	biplane_red_11.bm \
	biplane_red_12.bm \
	biplane_red_13.bm \
	biplane_red_14.bm \
	biplane_red_15.bm \
	biplane_blue_00.bm \
	biplane_blue_01.bm \
	biplane_blue_02.bm \
	biplane_blue_03.bm \
	biplane_blue_04.bm \
	biplane_blue_05.bm \
	biplane_blue_06.bm \
	biplane_blue_07.bm \
	biplane_blue_08.bm \
	biplane_blue_09.bm \
	biplane_blue_10.bm \
	biplane_blue_11.bm \
	biplane_blue_12.bm \
	biplane_blue_13.bm \
	biplane_blue_14.bm \
	biplane_blue_15.bm

WIDE_BITMAPS = \
	$(BIPLANE_BITMAPS) \
	bullet.bm \
	explosion0.bm \
	explosion1.bm \
	balloon.bm \
	cloud_left.bm \
	cloud_right.bm

# The C struct name of a .sbm file ends with _bm, not _sbm.

SCORE_BITMAPS = \
	space.sbm minus.sbm \
	digit0.sbm digit1.sbm digit2.sbm digit3.sbm digit4.sbm \
	digit5.sbm digit6.sbm digit7.sbm digit8.sbm digit9.sbm

# Non-generated source files, except $(PROG).c.
# The latter is excluded because it contains a main() function.
# The other C files listed here are also meant to be linked with
# unittsts.bin, which is a unit testing program, which comes with
# its own main() function.
#
NON_GEN_SRC = \
	Sprite.c \
	BiplaneMover.c \
	util.c \
	DirtyRect.c \
	DirtyRectList.c \
	KeyState.c \
	Score.c \
	Controller.c

NON_GEN_HEADERS = \
	PreShiftedBitmap.h \
	ThinBitmap.h \
	Sprite.h \
	BiplaneMover.h \
	util.h \
	constants.h \
	DirtyRect.h \
	DirtyRectList.h \
	KeyState.h \
	Score.h \
	Controller.h

GEN_SRC = $(WIDE_BITMAPS:.bm=.bm.c) $(SCORE_BITMAPS:.sbm=.sbm.c) title.c
SRC = $(NON_GEN_SRC) $(GEN_SRC)

GEN_HEADERS =

ifeq "$(TARGET)" "coco"
DECBFILE_CFLAGS = -I $(DECBFILE_LIB_DIR)
DECBFILE_LDFLAGS = -L $(DECBFILE_LIB_DIR) -ldecbfile
endif

CARDGAME_CFLAGS = -I $(CARDGAME_LIB_DIR)
CARDGAME_LDFLAGS = -L $(CARDGAME_LIB_DIR) -lcardgame

BCONTROL_CFLAGS = -I $(BCONTROL_LIB_DIR)
BCONTROL_LDFLAGS = -L $(BCONTROL_LIB_DIR) -lbcontrol

LIBS = $(DECBFILE_LDFLAGS) $(CARDGAME_LDFLAGS) $(BCONTROL_LDFLAGS)

NON_GEN_OBJ = $(NON_GEN_SRC:.c=.o)
GEN_OBJ = $(GEN_SRC:.c=.o)
OBJ = $(NON_GEN_OBJ) $(GEN_OBJ)

DSK = $(PACKAGE)-$(VERSION)-$(TARGET).dsk

all: $(DSK)

# title.xpm must be generated from title.xcf (typically with the GIMP,
# by exporting in XPM format, with alpha threshold 127).
#
title.c: convert-title-image.pl title.xpm
	$(PERL) convert-title-image.pl title.xpm > $@

INSTALL_LOADER = install-coco-boot-loader

# --allocate-after-dir is passed to writecocofile so that the .app file
# is close to the directory track.
# --allocate-after-dir requires the version of writecocofile that ships
# with CMOC 0.1.100 or later.
#
$(DSK): $(PROG).app loader.bin
ifeq "$(TARGET)" "coco"
	$(PERL) -e 'print chr(255) x (35*18*256)' > $@
	$(INSTALL_LOADER) --force $@ loader.bin # code loaded and executed by DOS command
	$(WRITECOCOFILE) --allocate-after-dir --decb-name="$(PROGRAM_ALL_CAPS_8_CHAR)" --decb-ext=APP $@ $(PROG).app
	$(PERL) -e 'print "\r10 DOS\r"' > ,basic-loader.bas
	$(WRITECOCOFILE) --ascii --allocate-after-dir --decb-name="$(PROGRAM_ALL_CAPS_8_CHAR)" --decb-ext=BAS $@ ,basic-loader.bas
	rm ,basic-loader.bas
else
	@echo "ERROR: Distribution floppy not supported for target '$(TARGET)'."
	@false
endif

# Main application, in raw format, to be loaded by loader.c, which is executable by DOS command.
#
$(PROG).app: $(PROG).o $(OBJ) $(CARDGAME_LIB_DIR)/libcardgame.a $(BCONTROL_LIB_DIR)/libbcontrol.a
	$(CMOC) --raw --$(TARGET) $(ORG) --limit=$(APP_MAX_END) $(LDFLAGS) -o $(PROG).app $(PROG).o $(OBJ) $(LIBS)


# Check that the floppy image contains the expected directory entries.
#
.PHONY: check-floppy
check-floppy: $(DSK)
	$(WRITECOCOFILE) -d $(DSK) | $(GREP) -q -E "$(PROGRAM_ALL_CAPS_8_CHAR)\.APP.*data.*binary"

# Builds .BIN unit testing program to be run on a CoCo.
# TARGET must be "coco".
# 'NDEBUG=' should be passed to make, to get asserts.
#
UNITTSTS_OBJ = util.o Score.o
unittsts.bin: unittsts.o $(UNITTSTS_OBJ)
	$(CMOC) --coco $(CFLAGS) --org=2800 --limit=7C00 $(LDFLAGS) -o $@ unittsts.o $(UNITTSTS_OBJ) $(LIBS)

check: unittsts.bin check-floppy


# Pattern rules.

%.o: %.c $(GEN_HEADERS)
	$(CMOC) -c --$(TARGET) $(CFLAGS) -o $@ $<

%.bm.o: %.bm
	$(PERL) compile-bitmap.pl --format=shiftedc $< > $<.c
	$(CMOC) -c --$(TARGET) $(CFLAGS) -o $@ $<.c

# Needed to keep GNU Make from saying "Circular xxx.bm <- xxx.bm.o dependency dropped."
*.bm: ;

%.sbm.o: %.sbm
	$(PERL) compile-bitmap.pl --format=c $< > $<.c
	$(CMOC) -c --$(TARGET) $(CFLAGS) -o $@ $<.c

# Needed to keep GNU Make from saying "Circular xxx.sbm <- xxx.sbm.o dependency dropped."
*.sbm: ;


ifeq "$(TARGET)" "coco"
LOADER_FLAGS = --dos
else  # dragon:
LOADER_FLAGS = --dragon
endif

# Loader program to be loaded (from track 34) and run by the DOS command.
# This loader program MUST be relocatable, so we do not pass --no-relocate.
# It loads a file whose 11-character name must be consistent with $(EXE).
#
loader.bin: loader.c $(DECBFILE_LIB_DIR)/libdecbfile.a
	$(CMOC) $(LOADER_FLAGS) -o $@ \
		'-DEXECUTABLE_FILENAME="$(PROGRAM_ALL_CAPS_8_CHAR)APP"' \
		-DCLIENT_PROGRAM_START=0x$(APP_ENTRY) \
		--limit=0x3780 \
		$(WARNINGS) $(WERROR) $(INTERMEDIATE) $(OPTIM) \
		$(DECBFILE_CFLAGS) \
		loader.c \
		$(DECBFILE_LDFLAGS)

# Runs make with arguments $(RECURSION) in the library projects used by this package.
# Supports the -n (dry run) option.
# Example: make recurse RECURSION=clean
#
export TARGET
.PHONY: recurse
recurse:
ifeq "$(TARGET)" "coco"
	$(MAKE) -C $(DECBFILE_LIB_DIR) $(RECURSION)
endif
	$(MAKE) -C $(CARDGAME_LIB_DIR) $(RECURSION)
	$(MAKE) -C $(BCONTROL_LIB_DIR) $(RECURSION)

clean:
	rm -f loader.bin $(PROG).app $(PROG).o $(OBJ) \
			$(WIDE_BITMAPS:.bm=.bm.c) \
			$(SCORE_BITMAPS:.sbm=.sbm.c) \
			$(GEN_HEADERS) $(GEN_SRC) \
			unittsts.bin unittsts.o

distclean: clean
	rm -f $(SRC:.c=.d)

.PHONY: dist distcheck
dist:
	$(TAR) -czf $(PACKAGE)-$(VERSION).tar.gz --transform 's,^,$(PACKAGE)-$(VERSION)/,' $(DISTFILES)

ifeq "$(TARGET)" "coco"

distcheck: dist
	mkdir ,distcheck
	$(TAR) -C ,distcheck -xzf $(PACKAGE)-$(VERSION).tar.gz
	for x in decbfile cardgame bcontrol; do ln -s ../../../$$x ,distcheck/$(PACKAGE)-$(VERSION)/$$x; done
	$(MAKE) -C ,distcheck/$(PACKAGE)-$(VERSION) all check
	rm -fr ,distcheck
	@echo "SUCCESS: $(PACKAGE)-$(VERSION).tar.gz ready for distribution"

endif

DISTFILES = \
	COPYING \
	Makefile \
	loader.c \
	$(PROG).c \
	$(NON_GEN_SRC) \
	$(NON_GEN_HEADERS) \
	$(WIDE_BITMAPS) \
	$(SCORE_BITMAPS) \
	compile-bitmap.pl \
	convert-title-image.pl \
	title.xcf \
	title.xpm \
	unittsts.c

.PHONY: sizes
sizes: $(PROG).app
	@$(PERL) -e '$$sz=-s "$(PROG).app"; printf("$(PROG).app loads %u B in \$$%X..\$$%X.\n", $$sz, 0x$(APP_ENTRY), 0x$(APP_ENTRY) + $$sz - 1);'

# Prerequisites.

$(CARDGAME_LIB_DIR)/pmode4game.h:
	@test -f $@ || echo "ERROR: Symlink '$(dir $@)' missing from current directory." && false

$(BCONTROL_LIB_DIR)/Joystick.h:
	@test -f $@ || echo "ERROR: Symlink '$(dir $@)' missing from current directory." && false

$(DECBFILE_LIB_DIR)/runDOSLoader.h:
	@test -f $@ || echo "ERROR: Symlink '$(dir $@)' missing from current directory." && false

DEPENDENCY_HEADERS = \
	$(NON_GEN_HEADERS) $(GEN_HEADERS) \
	$(CARDGAME_LIB_DIR)/pmode4game.h \
	$(BCONTROL_LIB_DIR)/Joystick.h

ifeq "$(TARGET)" "coco"
DEPENDENCY_HEADERS += \
	$(DECBFILE_LIB_DIR)/runDOSLoader.h
endif

$(NON_GEN_OBJ): $(DEPENDENCY_HEADERS)
$(GEN_OBJ): $(DEPENDENCY_HEADERS)
