diff options
author | Juan J. Martinez <jjm@usebox.net> | 2023-11-05 11:22:55 +0000 |
---|---|---|
committer | Juan J. Martinez <jjm@usebox.net> | 2023-11-05 11:31:28 +0000 |
commit | 2fbdf974338bde8576efdae40a819a76b2391033 (patch) | |
tree | 64d41a37470143f142344f9a439d96de3e7918c2 /src | |
download | kitsunes-curse-2fbdf974338bde8576efdae40a819a76b2391033.tar.gz kitsunes-curse-2fbdf974338bde8576efdae40a819a76b2391033.zip |
Initial import of the open source release
Diffstat (limited to 'src')
52 files changed, 9309 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..eec64ea --- /dev/null +++ b/src/Makefile @@ -0,0 +1,107 @@ +TARGET=kitcurs + +LOADER_ADDR=512 +TMP_ADDR=3072 + +# see splib.h: BUF_ADDR + (TW * TH / 2) * (sprite th * 2 + 2) * max sprites +# 0x0100 + (8 * 8 / 2) * (3 * 2 + 2) * 10 +APP_ADDR=2816 +PAK_SLOP=32 + +LOADER_ADDR_HEX=$(shell printf "%x" $(LOADER_ADDR)) +TMP_ADDR_HEX=$(shell printf "%x" $(TMP_ADDR)) + +CC=sdcc +AS=sdasz80 +AR=sdcclib +CFLAGS=-mz80 --Werror -I../lib -I../generated --fsigned-char --std-sdcc99 --opt-code-speed +LDFLAGS=-L../lib -L. --data-loc 0 --no-std-crt0 --fomit-frame-pointer + +OUTPUT = ../build +OBJS = $(patsubst %.c,$(OUTPUT)/%.rel,$(wildcard *.c)) $(OUTPUT)/int.rel $(OUTPUT)/sound.rel +LIBS = ../lib/cpcrslib/*.lib ../lib/plw.lib ../lib/aplib.lib + +all: CFLAGS := $(CFLAGS) #-DDEBUG -DFENCE_DEBUG -DET_DEBUG +all: $(OUTPUT)/$(TARGET).dsk $(OUTPUT)/$(TARGET).cdt + chksize $(APP_ADDR) $(OUTPUT)/main.map + +release: CFLAGS := $(CFLAGS) --max-allocs-per-node 2000000 +release: $(OUTPUT)/$(TARGET).dsk $(OUTPUT)/$(TARGET).cdt + test -d release || mkdir release + cp $(OUTPUT)/$(TARGET).dsk $(OUTPUT)/$(TARGET).cdt release + chksize $(APP_ADDR) $(OUTPUT)/main.map + +cpce: all + cpce $(OUTPUT)/$(TARGET).dsk + +cpcec: all + cpcec $(OUTPUT)/$(TARGET).dsk + +winape: all + winape $(OUTPUT)/$(TARGET).dsk + +clk: all + clk $(OUTPUT)/$(TARGET).dsk + +rvm: all + rvm -b=cpc6128 -w -p -ns -i $(shell realpath $(OUTPUT)/$(TARGET).dsk) -c='run"$(TARGET)\n' + +$(OUTPUT)/%.rel: %.c + $(CC) $(CFLAGS) $(LDFLAGS) -c $< -o $@ + +$(OUTPUT)/%.rel: %.z80 + $(AS) -g -o $@ $< + +$(OUTPUT)/sound.rel: sound.z80 effects.z80 songs.z80 + +$(OUTPUT)/main.ap: crt0.s $(wildcard *.h) $(LIBS) $(OBJS) ../lib/plw_player.rel + rm -f $(OUTPUT)/main.map + $(AS) -g -o $(OUTPUT)/crt0.rel crt0.s + $(CC) $(CFLAGS) $(LDFLAGS) -laplib -lcpcrslib -lplw --code-loc $(APP_ADDR) $(OUTPUT)/crt0.rel ../lib/plw_player.rel $(OBJS) -o $(OUTPUT)/main.ihx + hex2bin -p 00 $(OUTPUT)/main.ihx + apultra -w 8192 -v $(OUTPUT)/main.bin $@ + +$(OUTPUT)/loader.bin: loader.s turboload.s $(OUTPUT)/loading.bin $(OUTPUT)/main.ap + echo "DISK = 1" > $(OUTPUT)/loader.opt + echo "APP_EP = 0x$(shell awk ' /_main_init/ { print $$1 } ' $(OUTPUT)/main.map)" >> $(OUTPUT)/loader.opt + echo "TMP_ADDR = $(TMP_ADDR)" >> $(OUTPUT)/loader.opt + echo "SCRX_SIZE = $(shell stat -c '%s' $(OUTPUT)/loading.bin)" >> $(OUTPUT)/loader.opt + echo "APP_ADDR = $(APP_ADDR)" >> $(OUTPUT)/loader.opt + echo "APP_SIZE = $(shell stat -c '%s' $(OUTPUT)/main.bin)" >> $(OUTPUT)/loader.opt + echo "APP_SIZE_PAK = $(shell stat -c '%s' $(OUTPUT)/main.ap)" >> $(OUTPUT)/loader.opt + echo "LOADER_ADDR = $(LOADER_ADDR)" >> $(OUTPUT)/loader.opt + echo "PAK_SLOP = $(PAK_SLOP)" >> $(OUTPUT)/loader.opt + $(AS) -g -o $(OUTPUT)/loader.rel $< + $(CC) $(CFLAGS) $(LDFLAGS) --code-loc $(LOADER_ADDR) -laplib -o $(OUTPUT)/loader.ihx $(OUTPUT)/loader.rel + hex2bin -p 00 $(OUTPUT)/loader.ihx + echo "DISK = 0" >> $(OUTPUT)/loader.opt + $(AS) -g -o $(OUTPUT)/loader.rel $< + $(CC) $(CFLAGS) $(LDFLAGS) --code-loc $(LOADER_ADDR) -laplib -o $(OUTPUT)/loader_disk.ihx $(OUTPUT)/loader.rel + hex2bin -p 00 $(OUTPUT)/loader_disk.ihx + +$(OUTPUT)/$(TARGET).dsk: $(OUTPUT)/loader.bin + cp $(OUTPUT)/loader_disk.bin $(OUTPUT)/$(TARGET) + idsk $@ -n -t 1 -i $(OUTPUT)/$(TARGET) -e $(LOADER_ADDR_HEX) -c $(LOADER_ADDR_HEX) > /dev/null + rm -f $(OUTPUT)/$(TARGET) + cp $(OUTPUT)/loading.bin $(OUTPUT)/main.bi0 + idsk $@ -t 1 -i $(OUTPUT)/main.bi0 -c $(TMP_ADDR_HEX) -s > /dev/null + rm -f $(OUTPUT)/main.bi0 + cp $(OUTPUT)/main.ap $(OUTPUT)/main.bi1 + idsk $@ -t 1 -i $(OUTPUT)/main.bi1 -c $(shell printf "%x" $(shell expr $(LOADER_ADDR) + 1024)) -s > /dev/null + rm -f $(OUTPUT)/main.bi1 + +$(OUTPUT)/$(TARGET).cdt: $(OUTPUT)/loader.bin + 2cdt -s 0 -n -X $(LOADER_ADDR) -L $(LOADER_ADDR) -r $(TARGET) $< $@ > /dev/null + 2cdt -m 2 $(OUTPUT)/loading.bin $@ > /dev/null + 2cdt -m 2 $(OUTPUT)/main.ap $@ > /dev/null + +.PHONY: clean all cleanall release +clean: + rm -f $(OUTPUT)/* + +cleanall: + make clean + make -C ../tools clean + make -C ../lib clean + +include Makefile.deps diff --git a/src/cpcfirm.inc b/src/cpcfirm.inc new file mode 100644 index 0000000..88a9f1f --- /dev/null +++ b/src/cpcfirm.inc @@ -0,0 +1,227 @@ +kl_probe_rom = 0xb915 +kl_choke_off = 0xbcc8 +kl_rom_walk = 0xbccb +kl_init_back = 0xbcce +kl_log_ext = 0xbcd1 +kl_find_command = 0xbcd4 +kl_new_framefly = 0xbcd7 +kl_add_framefly = 0xbcda +kl_del_framefly = 0xbcdd +kl_new_fast_ticker = 0xbce0 +kl_add_fast_ticker = 0xbce3 +kl_del_fast_ticker = 0xbce6 +kl_add_ticker = 0xbce9 +kl_del_ticker = 0xbcec +kl_init_event = 0xbcef +kl_event = 0xbcf2 +kl_sync_reset = 0xbcf5 +kl_del_synchronous = 0xbcf8 +kl_next_sync = 0xbcfb +kl_do_sync = 0xbcfe +kl_done_sync = 0xbd01 +kl_event_disable = 0xbd04 +kl_event_enable = 0xbd07 +kl_disarm_event = 0xbd0a +kl_time_please = 0xbd0d +kl_time_set = 0xbd10 + +km_initialise = 0xbb00 +km_reset = 0xbb03 +km_wait_char = 0xbb06 +km_read_char = 0xbb09 +km_char_return = 0xbb0c +km_set_expand = 0xbb0f +km_get_expand = 0xbb12 +km_exp_buffer = 0xbb15 +km_wait_key = 0xbb18 +km_read_key = 0xbb1b +km_test_key = 0xbb1e +km_get_state = 0xbb21 +km_get_joystick = 0xbb24 +km_set_translate = 0xbb27 +km_get_translate = 0xbb2a +km_set_shift = 0xbb2d +km_get_shift = 0xbb30 +km_set_control = 0xbb33 +km_get_control = 0xbb36 +km_set_repeat = 0xbb39 +km_get_repeat = 0xbb3c +km_set_delay = 0xbb3f +km_get_delay = 0xbb42 +km_arm_break = 0xbb45 +km_disarm_break = 0xbb48 +km_break_event = 0xbb4b + +txt_initialise = 0xbb4e +txt_reset = 0xbb51 +txt_vdu_enable = 0xbb54 +txt_vdu_disable = 0xbb57 +txt_output = 0xbb5a +txt_wr_char = 0xbb5d +txt_rd_char = 0xbb60 +txt_set_graphic = 0xbb63 +txt_win_enable = 0xbb66 +txt_get_window = 0xbb69 +txt_clear_window = 0xbb6c +txt_set_column = 0xbb6f +txt_set_row = 0xbb72 +txt_set_cursor = 0xbb75 +txt_get_cursor = 0xbb78 +txt_cur_enable = 0xbb7b +txt_cur_disable = 0xbb7e +txt_cur_on = 0xbb81 +txt_cur_off = 0xbb84 +txt_validate = 0xbb87 +txt_place_cursor = 0xbb8a +txt_remove_cursor = 0xbb8d +txt_set_pen = 0xbb90 +txt_get_pen = 0xbb93 +txt_set_paper = 0xbb96 +txt_get_paper = 0xbb99 +txt_inverse = 0xbb9c +txt_set_back = 0xbb9f +txt_get_back = 0xbba2 +txt_get_matrix = 0xbba5 +txt_set_matrix = 0xbba8 +txt_set_m_table = 0xbbab +txt_get_m_table = 0xbbae +txt_get_controls = 0xbbb1 +txt_str_select = 0xbbb4 +txt_swap_streams = 0xbbb7 + +gra_initialise = 0xbbba +gra_reset = 0xbbbd +gra_move_absolute = 0xbbc0 +gra_move_relative = 0xbbc3 +gra_ask_cursor = 0xbbc6 +gra_set_origin = 0xbbc9 +gra_get_origin = 0xbbcc +gra_win_width = 0xbbcf +gra_win_height = 0xbbd2 +gra_get_w_width = 0xbbd5 +gra_get_w_height = 0xbbd8 +gra_clear_window = 0xbbdb +gra_set_pen = 0xbbde +gra_get_pen = 0xbbe1 +gra_set_paper = 0xbbe4 +gra_get_paper = 0xbbe7 +gra_plot_absolute = 0xbbea +gra_plot_relative = 0xbbed +gra_test_absolute = 0xbbf0 +gra_test_relative = 0xbbf3 +gra_line_absolute = 0xbbf6 +gra_line_relative = 0xbbf9 +gra_wr_char = 0xbbfc + + +scr_initialise = 0xbbff +scr_reset = 0xbc02 +scr_set_offset = 0xbc05 +scr_set_base = 0xbc08 +scr_get_location = 0xbc0b +scr_set_mode = 0xbc0e +scr_get_mode = 0xbc11 +scr_clear = 0xbc14 +scr_char_limits = 0xbc17 +scr_char_position = 0xbc1a +scr_dot_position = 0xbc1d +scr_next_byte = 0xbc20 +scr_prev_byte = 0xbc23 +scr_next_line = 0xbc26 +scr_prev_line = 0xbc29 +scr_ink_encode = 0xbc2c +scr_ink_decode = 0xbc2f +scr_set_ink = 0xbc32 +scr_get_ink = 0xbc35 +scr_set_border = 0xbc38 +scr_get_border = 0xbc3b +scr_set_flashing = 0xbc3e +scr_get_flashing = 0xbc41 +scr_fill_box = 0xbc44 +scr_flood_box = 0xbc17 +scr_char_invert = 0xbc4a +scr_hw_roll = 0xbc4d +scr_sw_roll = 0xbc50 +scr_unpack = 0xbc53 +scr_repack = 0xbc56 +scr_access = 0xbc59 +scr_pixels = 0xbc5c +scr_horizontal = 0xbc5f +scr_vertical = 0xbc62 + + +cas_initialise = 0xbc65 +cas_set_speed = 0xbc68 +cas_noisy = 0xbc6b +cas_start_motor = 0xbc6e +cas_stop_motor = 0xbc71 +cas_restore_motor = 0xbc74 +cas_in_open = 0xbc77 +cas_in_close = 0xbc7a +cas_in_abandon = 0xbc7d +cas_in_char = 0xbc80 +cas_in_direct = 0xbc83 +cas_return = 0xbc86 +cas_test_eof = 0xbc89 +cas_out_open = 0xbc8c +cas_out_close = 0xbc8f +cas_out_abandon = 0xbc92 +cas_out_char = 0xbc95 +cas_out_direct = 0xbc98 +cas_catalog = 0xbc9b +cas_write = 0xbc9e +cas_read = 0xbca1 +cas_check = 0xbca4 + +sound_reset = 0xbca7 +sound_queue = 0xbcaa +sound_check = 0xbcad +sound_arm_event = 0xbcb0 +sound_release = 0xbcb3 +sound_hold = 0xbcb6 +sound_continue = 0xbcb9 +sound_ampl_envelope = 0xbcbc +sound_tone_envelope = 0xbcbf +sound_a_address = 0xbcc2 +sound_t_address = 0xbcc5 + + +mc_boot_program = 0xbd13 +mc_start_program = 0xbd16 +mc_wait_flyback = 0xbd19 +mc_set_mode = 0xbd1c +mc_screen_offset = 0xbd1f +mc_clear_inks = 0xbd22 +mc_set_inks = 0xbd25 +mc_reset_printer = 0xbd28 +mc_print_char = 0xbd2b +mc_busy_printer = 0xbd2e +mc_send_printer = 0xbd31 +mc_sound_register = 0xbd34 +mc_jump_restore = 0xbd37 + +bios_set_message = 0xc033 +bios_setup_disc = 0xc036 +bios_select_format = 0xc039 +bios_read_sector = 0xc03c +bios_write_sector = 0xc03f +bios_format_track = 0xc042 +bios_move_track = 0xc045 +bios_get_status = 0xc048 +bios_set_retry_count = 0xc04b +bios_get_sector_data = 0xc56c + +; 664 + 6128 only +km_set_locks = 0xbd3a +km_flush = 0xbd3d +txt_ask_state = 0xbd40 +gra_default = 0xbd43 +gra_set_back = 0xbd46 +gra_set_first = 0xbd49 +gra_set_line_mask = 0xbd4c +gra_from_user = 0xbd4f +gra_fill = 0xbd52 +scr_set_position = 0xbd55 +mc_print_translation = 0xbd58 +kl_bank_switch = 0xbd5b ; 6128 only + diff --git a/src/crt0.s b/src/crt0.s new file mode 100644 index 0000000..e35d57c --- /dev/null +++ b/src/crt0.s @@ -0,0 +1,37 @@ +;; +;; Kitsune's Curse +;; Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> +;; +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. +;; +.module crt0 +.globl _main +.globl _main_init + + .area _HOME + .area _CODE + .area _DATA + .area _BSEG + .area _BSS + .area _HEAP + .area _INITIALIZER + + .area _CODE + +_main_init:: + call _main + +halt0: + halt + jr halt0 diff --git a/src/data.c b/src/data.c new file mode 100644 index 0000000..1a751b9 --- /dev/null +++ b/src/data.c @@ -0,0 +1,22 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +// only data + +#define LOCAL +#include "data.h" + diff --git a/src/data.h b/src/data.h new file mode 100644 index 0000000..ad65986 --- /dev/null +++ b/src/data.h @@ -0,0 +1,45 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _DATA_H +#define _DATA_H + +#include "songs_pak.h" +#include "palette.h" +#include "font.h" +#include "menubg.h" +#include "tiles.h" +#include "stage.h" +#include "player.h" +#include "explo.h" +#include "splash.h" +#include "items.h" +#include "doors.h" +#include "platform.h" +#include "switch.h" +#include "oni.h" +#include "ninja.h" +#include "spirit.h" +#include "flame.h" +#include "vampire.h" +#include "spider.h" +#include "demon.h" +#include "fireball.h" +#include "cloud.h" + +#endif // _DATA_H + diff --git a/src/effects.z80 b/src/effects.z80 new file mode 100644 index 0000000..2b0129c --- /dev/null +++ b/src/effects.z80 @@ -0,0 +1,351 @@ +; Sound Effects for song effects, version 2.0, generated by Arkos Tracker 2.
+
+effects_SoundEffects:
+effects_SoundEffectsDisarkGenerateExternalLabel:
+
+; The sound effects, starting at 1.
+effects_SoundEffectsDisarkPointerRegionStart0:
+ .dw effects_SoundEffects_Sound1 ; Sound effect 1.
+ .dw effects_SoundEffects_Sound2 ; Sound effect 2.
+ .dw effects_SoundEffects_Sound3 ; Sound effect 3.
+ .dw effects_SoundEffects_Sound4 ; Sound effect 4.
+ .dw effects_SoundEffects_Sound5 ; Sound effect 5.
+ .dw effects_SoundEffects_Sound6 ; Sound effect 6.
+ .dw effects_SoundEffects_Sound7 ; Sound effect 7.
+ .dw effects_SoundEffects_Sound8 ; Sound effect 8.
+ .dw effects_SoundEffects_Sound9 ; Sound effect 9.
+ .dw effects_SoundEffects_Sound10 ; Sound effect 10.
+effects_SoundEffectsDisarkPointerRegionEnd0:
+
+effects_SoundEffectsDisarkByteRegionStart1:
+; Sound effect 1.
+effects_SoundEffects_Sound1:
+ .db 0 ; Speed
+
+effects_SoundEffects_Sound1_Loop: .db 57 ; Soft only. Volume: 14.
+ .dw 1280 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1024 ; Software period.
+
+ .db 25 ; Soft only. Volume: 6.
+ .dw 1024 ; Software period.
+
+ .db 4 ; End of the sound effect.
+
+; Sound effect 2.
+effects_SoundEffects_Sound2:
+ .db 0 ; Speed
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 512 ; Software period.
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 512 ; Software period.
+
+ .db 53 ; Soft only. Volume: 13.
+ .dw 256 ; Software period.
+
+ .db 45 ; Soft only. Volume: 11.
+ .dw 32 ; Software period.
+
+effects_SoundEffects_Sound2_Loop: .db 41 ; Soft only. Volume: 10.
+ .dw 32 ; Software period.
+
+ .db 4 ; End of the sound effect.
+
+; Sound effect 3.
+effects_SoundEffects_Sound3:
+ .db 0 ; Speed
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 96 ; Software period.
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 98 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 100 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 104 ; Software period.
+
+effects_SoundEffects_Sound3_Loop: .db 41 ; Soft only. Volume: 10.
+ .dw 100 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 104 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 100 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 104 ; Software period.
+
+ .db 33 ; Soft only. Volume: 8.
+ .dw 104 ; Software period.
+
+ .db 33 ; Soft only. Volume: 8.
+ .dw 104 ; Software period.
+
+ .db 4 ; End of the sound effect.
+
+; Sound effect 4.
+effects_SoundEffects_Sound4:
+ .db 0 ; Speed
+
+effects_SoundEffects_Sound4_Loop: .db 41 ; Soft only. Volume: 10.
+ .dw 18 ; Software period.
+
+ .db 33 ; Soft only. Volume: 8.
+ .dw 16 ; Software period.
+
+ .db 17 ; Soft only. Volume: 4.
+ .dw 16 ; Software period.
+
+ .db 9 ; Soft only. Volume: 2.
+ .dw 16 ; Software period.
+
+ .db 4 ; End of the sound effect.
+
+; Sound effect 5.
+effects_SoundEffects_Sound5:
+ .db 0 ; Speed
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 768 ; Software period.
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 768 ; Software period.
+
+effects_SoundEffects_Sound5_Loop: .db 53 ; Soft only. Volume: 13.
+ .dw 1536 ; Software period.
+
+ .db 53 ; Soft only. Volume: 13.
+ .dw 1536 ; Software period.
+
+ .db 1 ; Soft only. Volume: 0.
+ .dw 768 ; Software period.
+
+ .db 1 ; Soft only. Volume: 0.
+ .dw 768 ; Software period.
+
+ .db 53 ; Soft only. Volume: 13.
+ .dw 1536 ; Software period.
+
+ .db 53 ; Soft only. Volume: 13.
+ .dw 1536 ; Software period.
+
+ .db 4 ; End of the sound effect.
+
+; Sound effect 6.
+effects_SoundEffects_Sound6:
+ .db 0 ; Speed
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 2048 ; Software period.
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 1792 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1280 ; Software period.
+
+effects_SoundEffects_Sound6_Loop: .db 41 ; Soft only. Volume: 10.
+ .dw 768 ; Software period.
+
+ .db 33 ; Soft only. Volume: 8.
+ .dw 1280 ; Software period.
+
+ .db 33 ; Soft only. Volume: 8.
+ .dw 512 ; Software period.
+
+ .db 25 ; Soft only. Volume: 6.
+ .dw 256 ; Software period.
+
+ .db 17 ; Soft only. Volume: 4.
+ .dw 256 ; Software period.
+
+ .db 4 ; End of the sound effect.
+
+; Sound effect 7.
+effects_SoundEffects_Sound7:
+ .db 0 ; Speed
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 128 ; Software period.
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 128 ; Software period.
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 64 ; Software period.
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 96 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 128 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 128 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 64 ; Software period.
+
+effects_SoundEffects_Sound7_Loop: .db 41 ; Soft only. Volume: 10.
+ .dw 96 ; Software period.
+
+ .db 1 ; Soft only. Volume: 0.
+ .dw 64 ; Software period.
+
+ .db 1 ; Soft only. Volume: 0.
+ .dw 64 ; Software period.
+
+ .db 1 ; Soft only. Volume: 0.
+ .dw 64 ; Software period.
+
+ .db 1 ; Soft only. Volume: 0.
+ .dw 64 ; Software period.
+
+ .db 33 ; Soft only. Volume: 8.
+ .dw 128 ; Software period.
+
+ .db 33 ; Soft only. Volume: 8.
+ .dw 128 ; Software period.
+
+ .db 33 ; Soft only. Volume: 8.
+ .dw 64 ; Software period.
+
+ .db 33 ; Soft only. Volume: 8.
+ .dw 64 ; Software period.
+
+ .db 25 ; Soft only. Volume: 6.
+ .dw 64 ; Software period.
+
+ .db 17 ; Soft only. Volume: 4.
+ .dw 64 ; Software period.
+
+ .db 4 ; End of the sound effect.
+
+; Sound effect 8.
+effects_SoundEffects_Sound8:
+ .db 0 ; Speed
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 512 ; Software period.
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 512 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1024 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1024 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1280 ; Software period.
+
+effects_SoundEffects_Sound8_Loop: .db 33 ; Soft only. Volume: 8.
+ .dw 1536 ; Software period.
+
+ .db 33 ; Soft only. Volume: 8.
+ .dw 1792 ; Software period.
+
+ .db 25 ; Soft only. Volume: 6.
+ .dw 1792 ; Software period.
+
+ .db 17 ; Soft only. Volume: 4.
+ .dw 1792 ; Software period.
+
+ .db 4 ; End of the sound effect.
+
+; Sound effect 9.
+effects_SoundEffects_Sound9:
+ .db 0 ; Speed
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 512 ; Software period.
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 512 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1024 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1024 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1280 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1536 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1792 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1536 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1792 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1280 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1536 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1024 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 1024 ; Software period.
+
+ .db 33 ; Soft only. Volume: 8.
+ .dw 1536 ; Software period.
+
+effects_SoundEffects_Sound9_Loop: .db 33 ; Soft only. Volume: 8.
+ .dw 1536 ; Software period.
+
+ .db 25 ; Soft only. Volume: 6.
+ .dw 1536 ; Software period.
+
+ .db 17 ; Soft only. Volume: 4.
+ .dw 1536 ; Software period.
+
+ .db 4 ; End of the sound effect.
+
+; Sound effect 10.
+effects_SoundEffects_Sound10:
+ .db 0 ; Speed
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 496 ; Software period.
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 496 ; Software period.
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 384 ; Software period.
+
+ .db 57 ; Soft only. Volume: 14.
+ .dw 384 ; Software period.
+
+ .db 41 ; Soft only. Volume: 10.
+ .dw 352 ; Software period.
+
+effects_SoundEffects_Sound10_Loop: .db 41 ; Soft only. Volume: 10.
+ .dw 336 ; Software period.
+
+ .db 33 ; Soft only. Volume: 8.
+ .dw 336 ; Software period.
+
+ .db 17 ; Soft only. Volume: 4.
+ .dw 336 ; Software period.
+
+ .db 4 ; End of the sound effect.
+
+effects_SoundEffectsDisarkByteRegionEnd1:
diff --git a/src/entities.c b/src/entities.c new file mode 100644 index 0000000..47f68b6 --- /dev/null +++ b/src/entities.c @@ -0,0 +1,534 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include <stdint.h> +#include <string.h> + +#include "main.h" + +#define LOCAL +#include "entities.h" + +//#define ET_DEBUG + +struct st_entity *sp_free; +struct st_entity entities[MAX_ENTITIES]; + +void init_entities() +{ + uint8_t i; + + memset(entities, 0, sizeof(struct st_entity) * MAX_ENTITIES); + + for (i = 0; i < MAX_ENTITIES - 1; ++i) + entities[i].n = entities + i + 1; + + sp_used = NULL; + sp_free = entities; + sp_collect = 0; +} + +#pragma save +#pragma disable_warning 59 +uint8_t new_entity() +{ + // new entity on sp_new on success + + /* + if (!sp_free) + return 0; + + sp_new = sp_free; + sp_free = sp_free->n; + sp_new->n = sp_used; + sp_used = sp_new; + + return 1; + */ + + // *INDENT-OFF* + __asm; + + ; check MSB + ld a, (_sp_free + 1) + ld l, a + or a +#ifndef ET_DEBUG + ret z +#else + jr nz, new_entity_free + + ld hl, #0x4a + call _set_hw_border + + xor a + ld l, a + ret +new_entity_free: +#endif + + ld hl, (_sp_free) + ld (_sp_new), hl + + ld bc, #ET_SIZE_PT + + add hl, bc + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + ld (_sp_free), hl + + ld hl, (_sp_new) + add hl, bc + ld a, (_sp_used) + ld (hl), a + inc hl + ld a, (_sp_used + 1) + ld (hl), a + + ld hl, (_sp_new) + ld (_sp_used), hl + + ld l, #1 + __endasm; + // *INDENT-ON* +} +#pragma restore + +void destroy_entity() +{ + // entity on sp_it + sp_it->type = ET_UNUSED; + sp_collect++; +} + +void free_entities() +{ + /* + if (!sp_used) + return; + + // current, previous + for (sp_it = sp_it2 = sp_used; sp_it && sp_collect;) + { + if (sp_it->type == ET_UNUSED) + { + sp_collect--; + + if (sp_it == sp_used) + { + sp_it2 = sp_free; + sp_free = sp_used; + sp_used = sp_used->n; + sp_free->n = sp_it2; + sp_it = sp_it2 = sp_used; + continue; + } + else + { + sp_it2->n = sp_it->n; + sp_it->n = sp_free; + sp_free = sp_it; + sp_it = sp_it2->n; + continue; + } + } + sp_it2 = sp_it; + sp_it = sp_it->n; + } + */ + + // *INDENT-OFF* + __asm; + + ; check MSB + ld a, (_sp_used + 1) + or a + ret z + + ld hl, (_sp_used) + ld (_sp_it2), hl + ld (_sp_it), hl + + ld bc, #ET_SIZE_PT + +free_entities_loop: + ; hl holds it + + ; check MSB + ld a, (_sp_collect) + or a + ret z + ld a, h + or a + ret z + + ld a, (hl) + or a + jr z, free_entities_unused + + ; next + + ; it2 is it + ld (_sp_it2), hl + + ; it to it->n + add hl, bc + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + ld (_sp_it), hl + + jr free_entities_loop + +free_entities_unused: + ld a, (_sp_collect) + dec a + ld (_sp_collect), a + + ld a, (_sp_used) + cp l + jr nz, free_entities_not_used + ld a, (_sp_used + 1) + cp h + jr nz, free_entities_not_used + + ; it2 = free + ld hl, (_sp_free) + ld (_sp_it2), hl + + ; free = used + ld hl, (_sp_used) + ld (_sp_free), hl + + ; used = used->n + add hl, bc + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + ld (_sp_used), hl + + ; free->n = it2 + ld hl, (_sp_it2) + ex de, hl + ld hl, (_sp_free) + add hl, bc + ld (hl), e + inc hl + ld (hl), d + + ; it = it2 = used + ld hl, (_sp_used) + ld (_sp_it2), hl + ld (_sp_it), hl + + jr free_entities_loop + +free_entities_not_used: + ; it2->n = it->n + add hl, bc + ld e, (hl) + inc hl + ld d, (hl) + ld hl, (_sp_it2) + add hl, bc + ld (hl), e + inc hl + ld (hl), d + + ; it->n = free + ld hl, (_sp_free) + ex de, hl + ld hl, (_sp_it) + add hl, bc + ld (hl), e + inc hl + ld (hl), d + + ; free = it + ld hl, (_sp_it) + ld (_sp_free), hl + + ; it = it2->n + ld hl, (_sp_it2) + add hl, bc + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + ld (_sp_it), hl + + jp free_entities_loop + __endasm; + // *INDENT-ON* +} + +#pragma save +#pragma disable_warning 85 +#pragma disable_warning 59 +uint8_t check_for_point(struct st_entity *s, uint8_t x, uint8_t y, uint8_t w, uint8_t h) +{ + // return (y < s->y + h && y >= s->y && x < s->x + w && x >= s->x); + // *INDENT-OFF* + __asm; + + ld hl, #2 + add hl, sp + + ld e, (hl) + inc hl + ld d, (hl) + + ex de, hl + ld bc, #4 + add hl, bc + ld a, (hl) + inc hl + inc hl + ld h, (hl) + ld l, a + ex de, hl + + inc hl + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + + ; de s (x, y) + ; bc (x, y) + ; hl (w, h) + + ld a, b + cp d + jr c, check_for_point_fail + + ld a, d + add h + cp b + jr c, check_for_point_fail + + ld a, c + cp e + jr c, check_for_point_fail + + ld a, e + add l + cp c + jr c, check_for_point_fail + + ld l, #1 + ret + +check_for_point_fail: + ld l, #0 + + __endasm; + // *INDENT-ON* +} +#pragma restore + +void draw_entities() +{ + /* + draw_player(); + + for (sp_it = sp_used; sp_it; sp_it = sp_it->n) + { + it_x = &sp_it->x; + it_y = &sp_it->y; + it_ox = &sp_it->ox; + it_oy = &sp_it->oy; + it_frame = &sp_it->frame; + it_param = &sp_it->param; + it_extra = &sp_it->extra; + + sp_it->draw(); + } + */ + + // *INDENT-OFF* + __asm; + call _draw_player + + ld hl, (_sp_used) + + ld a, h + or l + jr z, draw_entities_done + +draw_entities_loop: + + ld (_sp_it), hl + + ld bc, #2 + + add hl, bc + ld (_it_frame), hl + add hl, bc + ld (_it_x), hl + inc hl + ld (_it_ox), hl + inc hl + ld (_it_y), hl + inc hl + ld (_it_oy), hl + inc hl + ld (_it_param), hl + inc hl + ld (_it_extra), hl + inc hl + add hl, bc + + push hl + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + call ___sdcc_call_hl + pop hl + + inc hl + inc hl + + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + or h + jr nz, draw_entities_loop + +draw_entities_done: + + __endasm; + // *INDENT-ON* +} + +void update_entities() +{ + /* + for (sp_it = sp_used; sp_it; sp_it = sp_it->n) + { + if (sp_it->type == ET_UNUSED) + continue; + + it_frame = &sp_it->frame; + it_x = &sp_it->x; + it_y = &sp_it->y; + + sp_it->ox = *it_x; + it_ox = &sp_it->ox; + sp_it->oy = *it_y; + it_oy = &sp_it->oy; + + it_delay = &sp_it->delay; + it_param = &sp_it->param; + it_extra = &sp_it->extra; + + sp_it->update(); + } + + if (sp_collect) + free_entities(); + + update_player(); + */ + // *INDENT-OFF* + __asm; + + ld hl, (_sp_used) + + ld a, h + or l + jr z, update_entities_done + +update_entities_loop: + + ld a, (hl) + or a + jr nz, update_entities_no_skip + + ld bc, #ET_SIZE_PT + jr update_entities_next + +update_entities_no_skip: + ld (_sp_it), hl + + inc hl + inc hl + ld (_it_frame), hl + inc hl + ld (_it_delay), hl + inc hl + ld (_it_x), hl + ld a, (hl) + inc hl + ld (_it_ox), hl + ld (hl), a + inc hl + ld (_it_y), hl + ld a, (hl) + inc hl + ld (_it_oy), hl + ld (hl), a + inc hl + ld (_it_param), hl + inc hl + ld (_it_extra), hl + inc hl + + push hl + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + call ___sdcc_call_hl + pop hl + + ld bc, #4 + +update_entities_next: + add hl, bc + + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + or h + jr nz, update_entities_loop + +update_entities_done: + ld a, (_sp_collect) + or a + jr z, update_entities_exit + + call _free_entities + +update_entities_exit: + call _update_player + + __endasm; + // *INDENT-ON* +} + diff --git a/src/entities.h b/src/entities.h new file mode 100644 index 0000000..4469896 --- /dev/null +++ b/src/entities.h @@ -0,0 +1,70 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _ENTITIES_H +#define _ENTITIES_H + +#include <stdint.h> + +#ifndef LOCAL +#define LOCAL extern +#endif + +struct st_entity +{ + uint8_t type; + uint8_t id; + uint8_t frame; + uint8_t delay; + uint8_t x; + uint8_t ox; + uint8_t y; + uint8_t oy; + uint8_t param; + uint8_t extra; + void (*update)(); + void (*draw)(); + struct st_entity *n; +}; + +// entitiy structure size to *n +#define ET_SIZE_PT 14 + +// define MAX_ENTITIES and entity types +#include "et_config.h" + +void init_entities(); +uint8_t new_entity(); +void destroy_entity(); +void free_entities(); + +uint8_t check_for_point(struct st_entity *s, uint8_t x, uint8_t y, uint8_t w, uint8_t h); + +void draw_entities(); +void update_entities(); + +LOCAL struct st_entity *sp_used, *sp_new; + +LOCAL struct st_entity *sp_it, *sp_it2; +LOCAL uint8_t sp_collect; + +LOCAL uint8_t *it_x, *it_y, *it_ox, *it_oy, *it_param, *it_frame, *it_delay, *it_extra, it_k; + +#undef LOCAL + +#endif // _ENTITIES_H + diff --git a/src/et_cloud.c b/src/et_cloud.c new file mode 100644 index 0000000..651fa1f --- /dev/null +++ b/src/et_cloud.c @@ -0,0 +1,82 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "splib.h" +#include "main.h" + +#include "maps.h" + +// generated +#include "cloud.h" + +#include "entities.h" +#include "et_cloud.h" +#include "et_player.h" + +void draw_cloud() +{ + it_k = *it_x & 1; + if (*it_param) + it_k ^= 1; + + put_sprite4(cloud[it_k], *it_x, *it_y, 3, *it_param & 128); + erase_sprite(*it_ox, *it_oy, 3); +} + +void update_cloud() +{ + if (*it_param) + { + if (is_map_blocked(*it_x + 11, *it_y + 22) + || is_map_blocked(*it_x + 11, *it_y + 7) + || is_map_blocked(*it_x + 11, *it_y + 2) + || *it_x > TW * TMW - 8 - 4) + goto cloud_change_dir; + else + *it_x += 1; + } + else + { + if (is_map_blocked(*it_x - 4, *it_y + 22) + || is_map_blocked(*it_x - 4, *it_y + 7) + || is_map_blocked(*it_x - 4, *it_y + 2) + || *it_x < 4) + goto cloud_change_dir; + else + *it_x -= 1; + } + + if (!magic && lives + && abs_sub(px, *it_x) < 64 && py != *it_y + && ( + (*it_param && px > *it_x) || (*it_param == 0 && px < *it_x) + )) + { + if (py > *it_y && !is_map_blocked(*it_x + 3, *it_y + 23)) + *it_y += 1; + + if (py < *it_y && !is_map_blocked(*it_x + 3, *it_y + 1)) + *it_y -= 1; + } + + if (check_for_player(24)) + { + player_hit(2); +cloud_change_dir: + *it_param ^= 128; + } +} diff --git a/src/et_cloud.h b/src/et_cloud.h new file mode 100644 index 0000000..d7641da --- /dev/null +++ b/src/et_cloud.h @@ -0,0 +1,25 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _CLOUD_H +#define _CLOUD_H + +void draw_cloud(); +void update_cloud(); + +#endif // _CLOUD_H + diff --git a/src/et_common.c b/src/et_common.c new file mode 100644 index 0000000..d361807 --- /dev/null +++ b/src/et_common.c @@ -0,0 +1,39 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "splib.h" +#include "main.h" + +#include "entities.h" +#include "et_player.h" + +void update_fixed_common() +{ + // limit and change direction + if ((*it_param & 127) == *it_extra) + { + *it_param &= 128; + *it_param ^= 128; + } + + ++(*it_param); + + if (*it_param & 128) + *it_x += 1; + else + *it_x -= 1; +} diff --git a/src/et_common.h b/src/et_common.h new file mode 100644 index 0000000..cf13028 --- /dev/null +++ b/src/et_common.h @@ -0,0 +1,24 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _COMMON_H +#define _COMMON_H + +void update_fixed_common(); + +#endif // _COMMON_H + diff --git a/src/et_config.h b/src/et_config.h new file mode 100644 index 0000000..d5dc8fd --- /dev/null +++ b/src/et_config.h @@ -0,0 +1,70 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef __ET_CONFIG_H +#define __ET_CONFIG_H + +#define MAX_ENTITIES 10 + +#define ET_IS_ENEMY(x) (x->type > ET_PLATFORM && x->type < ET_POTION) + +// id for non persistent entities +#define ETID_NP 255 + +// first ET in map +#define ET_FIRST ET_DOOR + +enum entity_type +{ + ET_UNUSED = 0, + + // adds extra info to the map, not real ETs + ET_FILL, + // links up/down maps + ET_LINK, + + // affect is_map_blocked + ET_DOOR, + ET_PLATFORM, + + // enemies + ET_SPIRIT, + ET_FLAME, + ET_VAMPIRE, + ET_ONI, + ET_NINJA, + ET_SPIDER, + ET_DEMON, + ET_CLOUD, + + // bg + ET_TORCH, + + // statics + ET_SWITCH, + ET_GEM, + ET_KEY, + ET_POTION, + ET_GTAIL, + + // not in map + ET_EXPLO, + ET_SPLASH, +}; + +#endif // __ET_CONFIG_H + diff --git a/src/et_demon.c b/src/et_demon.c new file mode 100644 index 0000000..fd54f8e --- /dev/null +++ b/src/et_demon.c @@ -0,0 +1,156 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include <string.h> + +#include "splib.h" +#include "plw.h" +#include "sound.h" +#include "main.h" + +#include "maps.h" + +// generated +#include "demon.h" +#include "fireball.h" + +#include "entities.h" +#include "et_demon.h" +#include "et_player.h" + +static const struct st_entity base = { + ET_EXPLO, + 0, 0, 0, 0, 0, 0, 0, + 0, // param + 0, // extra + update_fireball, + draw_fireball, + 0 +}; + +void draw_fireball() +{ + put_sprite4(fireball[player_frames[*it_frame]], *it_x, *it_y, 1, 0); + erase_sprite(*it_ox, *it_oy, 1); +} + +void update_fireball() +{ + if ((*it_delay)++ == 1) + { + *it_delay = 0; + if (++(*it_frame) > WALK_CYCLE) + *it_frame = WALK; + } + + if (*it_param) + { + if (is_map_blocked(*it_x + 11, *it_y + 8) + || *it_x > TW * TMW - 8 - 4) + goto fireball_done; + else + *it_x += 2; + } + else + { + if (is_map_blocked(*it_x - 4, *it_y + 8) + || *it_x < 4) + goto fireball_done; + else + *it_x -= 2; + } + + if (check_for_player(8)) + { + player_hit(2); +fireball_done: + erase_sprite(*it_ox, *it_oy, 1); + destroy_entity(); + } +} + +void new_fireball() +{ + if (!new_entity()) + return; + + memcpy(sp_new, &base, sizeof(base) - 2); + if (*it_param) + sp_new->x = *it_x + 8; + else + sp_new->x = *it_x - 8; + sp_new->param = *it_param; + sp_new->y = *it_y + 8; + + PLW_PlaySoundEffectP(EFX_MAGIC); +} + +void draw_demon() +{ + put_sprite4(demon[player_frames[*it_frame]], *it_x, *it_y, 3, *it_param & 128); + erase_sprite(*it_ox, *it_oy, 3); +} + +void update_demon() +{ + if ((*it_delay)++ == 2 || *it_delay == 11) + { + *it_delay = 0; + if (++(*it_frame) > WALK_CYCLE) + *it_frame = WALK; + } + + if (*it_extra < 72) + *it_extra += 1; + else if (abs_sub(py, *it_y) < 8 && + (*it_param && px > *it_x || !*it_param && px < *it_x)) + { + new_fireball(); + *it_extra = 0; + + *it_frame = WALK_CYCLE; + *it_delay = 3; + } + + if (*it_param) + { + if (is_map_blocked(*it_x + 11, *it_y + 8) + || !is_map_blocked(*it_x + 11, *it_y + 24) + || *it_x > TW * TMW - 8 - 4) + goto demon_change_dir; + else + *it_x += 1; + } + else + { + if (is_map_blocked(*it_x - 4, *it_y + 8) + || !is_map_blocked(*it_x - 4, *it_y + 24) + || *it_x < 4) + goto demon_change_dir; + else + *it_x -= 1; + } + + if (check_for_player(24)) + { + player_hit(1); +demon_change_dir: + *it_param ^= 128; + *it_delay = 0; + *it_frame = WALK; + } +} diff --git a/src/et_demon.h b/src/et_demon.h new file mode 100644 index 0000000..4c62811 --- /dev/null +++ b/src/et_demon.h @@ -0,0 +1,28 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _DEMON_H +#define _DEMON_H + +void draw_demon(); +void update_demon(); + +void draw_fireball(); +void update_fireball(); + +#endif // _DEMON_H + diff --git a/src/et_door.c b/src/et_door.c new file mode 100644 index 0000000..e764715 --- /dev/null +++ b/src/et_door.c @@ -0,0 +1,56 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "splib.h" +#include "plw.h" +#include "sound.h" +#include "main.h" + +#include "maps.h" + +// generated +#include "doors.h" + +#include "entities.h" +#include "et_player.h" +#include "et_door.h" + +void draw_door() { + if (is_invalid_tile2(get_tile_xy(*it_x >> 3, *it_y >> 3))) + for (it_k = 0; it_k < 16; it_k += 8) + put_sprite4(doors[*it_frame], *it_x, it_k + *it_y, 1, 0); + + if (is_invalid_tile2(get_tile_xy(*it_x >> 3, 2 + (*it_y >> 3)))) + for (it_k = 16; it_k < 32; it_k += 8) + put_sprite4(doors[*it_frame], *it_x, it_k + *it_y, 1, 0); +} + +void update_door() { + // is_map_blocked prevents this if no keys + if (!check_for_player(32)) + return; + + erase_sprite(*it_x, *it_y, 4); + destroy_entity(); + + update_persistence(sp_it->id); + --keys; + + draw_hud(); + + PLW_PlaySoundEffectP(EFX_DOOR); +} diff --git a/src/et_door.h b/src/et_door.h new file mode 100644 index 0000000..a1f39fd --- /dev/null +++ b/src/et_door.h @@ -0,0 +1,25 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _DOOR_H +#define _DOOR_H + +void draw_door(); +void update_door(); + +#endif // _DOOR_H + diff --git a/src/et_effect.c b/src/et_effect.c new file mode 100644 index 0000000..02f40dc --- /dev/null +++ b/src/et_effect.c @@ -0,0 +1,125 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include <string.h> + +#include "splib.h" +#include "plw.h" +#include "sound.h" +#include "main.h" + +// generated +#include "explo.h" +#include "splash.h" +#include "tiles.h" + +#include "entities.h" +#include "et_effect.h" + +static const struct st_entity base = { + ET_EXPLO, + 0, 0, 0, 0, 0, 0, 0, + 1, // param + 0, // extra + update_effect, + draw_explo, + 0 +}; + +void update_effect() +{ + if (++*it_delay > 1) + { + *it_delay = 0; + if (++*it_frame > *it_param) + { + destroy_entity(); + erase_sprite(*it_x, *it_y, 2); + } + } +} + +void draw_explo() +{ + put_sprite4(explo[*it_frame], *it_x, *it_y, 2, 0); +} + +void draw_splash() +{ + put_sprite4(splash[*it_frame], *it_x, *it_y, 2, 0); +} + +void new_explo() +{ + if (!new_entity()) + return; + + memcpy(sp_new, &base, sizeof(base) - 2); + sp_new->x = px; + sp_new->y = py + 6; + + if (player_h) + { + sp_new->y += 4; + if (sp_new->y > TH * TMH - 16) + sp_new->y = (uint8_t)(TH * TMH - 16); + } + + PLW_PlaySoundEffectP(EFX_MAGIC); +} + +void new_explo_et() +{ + if (!new_entity()) + return; + + memcpy(sp_new, &base, sizeof(base) - 2); + sp_new->x = *it_x; + sp_new->y = *it_y + 6; + + PLW_PlaySoundEffectP(EFX_MAGIC); +} + +void new_splash() +{ + if (!new_entity()) + return; + + memcpy(sp_new, &base, sizeof(base) - 2); + sp_new->draw = draw_splash; + sp_new->param = 2; + sp_new->x = px; + sp_new->y = py + 8; + + PLW_PlaySoundEffectP(EFX_SPLASH); +} + +void draw_torch() +{ + if (!*it_param) + put_tile(bgtiles[TORCH_BASE_TILE + *it_frame], get_tile_xy(*it_x, *it_y)); +} + +void update_tile_anim() +{ + if ((*it_param)++ > 2) + { + *it_param = 0; + if (++(*it_frame) == 3) + *it_frame = 0; + } +} diff --git a/src/et_effect.h b/src/et_effect.h new file mode 100644 index 0000000..a21d593 --- /dev/null +++ b/src/et_effect.h @@ -0,0 +1,35 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _EXPLO_H +#define _EXPLO_H + +#define TORCH_BASE_TILE 55 + +void draw_explo(); +void draw_splash(); +void update_explo(); + +void new_explo(); +void new_explo_et(); +void new_splash(); + +void draw_torch(); +void update_tile_anim(); + +#endif // _EXPLO_H + diff --git a/src/et_flame.c b/src/et_flame.c new file mode 100644 index 0000000..7f53667 --- /dev/null +++ b/src/et_flame.c @@ -0,0 +1,55 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "splib.h" +#include "main.h" + +// generated +#include "flame.h" + +#include "entities.h" +#include "et_flame.h" +#include "et_player.h" +#include "et_common.h" + +void draw_flame() +{ + put_sprite4(flame[*it_frame], *it_x, *it_y, 2, *it_param & 128); + erase_sprite(*it_ox, *it_oy, 2); +} + +void update_flame() +{ + if ((*it_delay)++ == 2) + { + *it_delay = 0; + if (++(*it_frame) > 2) + *it_frame = 0; + } + + update_fixed_common(); + + if (check_for_player(16)) + { + player_hit(1); + it_k = *it_param & 128; + *it_param = *it_extra - (*it_param & 127); + *it_param |= it_k ^ 128; + *it_delay = 0; + *it_frame = WALK; + } +} diff --git a/src/et_flame.h b/src/et_flame.h new file mode 100644 index 0000000..b65806d --- /dev/null +++ b/src/et_flame.h @@ -0,0 +1,25 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _FLAME_H +#define _FLAME_H + +void draw_flame(); +void update_flame(); + +#endif // _FLAME_H + diff --git a/src/et_ninja.c b/src/et_ninja.c new file mode 100644 index 0000000..e1e8bb1 --- /dev/null +++ b/src/et_ninja.c @@ -0,0 +1,127 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "splib.h" +#include "main.h" + +#include "maps.h" + +// generated +#include "ninja.h" + +#include "entities.h" +#include "et_ninja.h" +#include "et_player.h" +#include "et_effect.h" + +void draw_ninja() +{ + if (*it_extra) + return; + + put_sprite4(ninja[player_frames[*it_frame]], *it_x, *it_y, 3, *it_param); + erase_sprite(*it_ox, *it_oy, 3); +} + +uint8_t try_magic() +{ + uint8_t target = *it_x; + + if (*it_x == TW * TMW - 8 - 2 + || *it_x == 2) + return 0; + + for (it_k = 0; it_k < 6; it_k++) + { + if (*it_param) + target += 8; + else + target -= 8; + + if (is_map_blocked(target, *it_y + 8) + || is_map_deadly(target, *it_y + 24) + || target < 4 + || target > TW * TMW - 8 - 4) + break; + + if (is_map_blocked(target, *it_y + 24)) + { + erase_sprite(*it_ox, *it_oy, 3); + new_explo_et(); + *it_x = (target / TW) * TW; + *it_extra = COOL_DOWN + 2; + return 1; + } + } + + return 0; +} + +void update_ninja() +{ + if ((*it_delay)++ == 2) + { + *it_delay = 0; + if (++(*it_frame) > WALK_CYCLE) + *it_frame = WALK; + } + + if (*it_extra) + { + --(*it_extra); + if (*it_extra == 0) + new_explo_et(); + return; + } + + if (*it_param) + { + if (is_map_blocked(*it_x + 8, *it_y + 8) || *it_x == TW * TMW - 8 - 2 + || !is_map_blocked(*it_x + 8, *it_y + 24) + || is_map_deadly(*it_x + 8, *it_y + 24)) + { + if (try_magic()) + return; + goto ninja_change_dir; + } + else + *it_x += 1; + } + else + { + if (is_map_blocked(*it_x - 1, *it_y + 8) || *it_x == 2 + || !is_map_blocked(*it_x - 1, *it_y + 24) + || is_map_deadly(*it_x - 1, *it_y + 24)) + { + if (try_magic()) + return; + goto ninja_change_dir; + } + else + *it_x -= 1; + } + + if (*it_extra == 0 && check_for_player(24)) + { + player_hit(1); + +ninja_change_dir: + *it_param ^= 128; + *it_delay = 0; + *it_frame = WALK; + } +} diff --git a/src/et_ninja.h b/src/et_ninja.h new file mode 100644 index 0000000..5544374 --- /dev/null +++ b/src/et_ninja.h @@ -0,0 +1,25 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _NINJA_H +#define _NINJA_H + +void draw_ninja(); +void update_ninja(); + +#endif // _NINJA_H + diff --git a/src/et_oni.c b/src/et_oni.c new file mode 100644 index 0000000..5a6a899 --- /dev/null +++ b/src/et_oni.c @@ -0,0 +1,84 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "splib.h" +#include "plw.h" +#include "sound.h" +#include "main.h" + +#include "maps.h" + +// generated +#include "oni.h" + +#include "entities.h" +#include "et_oni.h" +#include "et_player.h" + +void draw_oni() { + put_sprite4(oni[*it_frame], *it_x, *it_y, 2, *it_param); + erase_sprite(*it_ox, *it_oy, 2); +} + +void update_oni() { + if (++(*it_delay) > 32) + { + if ((*it_param && *it_x < px) + || (!*it_param && *it_x > px)) + { + *it_extra = 4; + *it_frame = 1; + + if (*it_delay == 33) + PLW_PlaySoundEffectP(EFX_ONI); + + if (*it_delay > 48) + { + *it_delay = 0; + *it_frame = 0; + *it_extra = 2; + } + } + else + --(*it_delay); + } + + if (*it_param) + { + if (is_map_blocked(*it_x + 11, *it_y + 8) || *it_x > TW * TMW - 8 - 4) + goto oni_change_dir; + else + *it_x += *it_extra; + } + else + { + if (is_map_blocked(*it_x - 4, *it_y + 8) || *it_x < 4) + goto oni_change_dir; + else + *it_x -= *it_extra; + } + + if (check_for_player(16)) + { + player_hit(1); + *it_frame = 0; + *it_extra = 2; + *it_delay = 0; +oni_change_dir: + *it_param ^= 128; + } +} diff --git a/src/et_oni.h b/src/et_oni.h new file mode 100644 index 0000000..ac09bc5 --- /dev/null +++ b/src/et_oni.h @@ -0,0 +1,25 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _ONI_H +#define _ONI_H + +void draw_oni(); +void update_oni(); + +#endif // _ONI_H + diff --git a/src/et_pickups.c b/src/et_pickups.c new file mode 100644 index 0000000..58e3b25 --- /dev/null +++ b/src/et_pickups.c @@ -0,0 +1,191 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "splib.h" +#include "plw.h" +#include "sound.h" +#include "main.h" +#include "int.h" + +#include "maps.h" + +// generated +#include "items.h" +#include "tiles.h" + +#include "entities.h" +#include "et_player.h" +#include "et_pickups.h" + +void draw_pickup() +{ + if (!is_invalid_tile2(get_tile_xy(*it_x >> 3, *it_y >> 3))) + return; + + put_sprite4(items[sp_it->type - ET_GEM], *it_x, *it_y, 2, 0); +} + +void draw_gtail() +{ + put_sprite4(items[0], *it_x, *it_y, 2, 0); + erase_sprite(*it_ox, *it_oy, 2); +} + +static const int8_t gtail_y[9] = { + 0, 0, -1, -1, -2, -3, -2, -1, -1 +}; + +static const int8_t gtails[16] = { + -1, 0, + -1, -1, + 0, -1, + 1, -1, + 1, 0, + 1, 1, + 0, 1, + -1, 1, + }; + +void _gtail_effect() +{ + uint8_t i; + int8_t x, y; + + for (i = 0; i < 5; ++i) + { + for (it_k = 0; it_k < 16; it_k += 2) + { + x = (gtails[it_k] << i); + y = (gtails[it_k + 1] << i); + + put_sprite4(items[0], *it_x + x, *it_y + y, 2, 0); + + if (i) + { + x >>= 1; + y >>= 1; + erase_sprite(*it_x + x, *it_y + y, 2); + } + } + + if (i == 2) + set_hw_ink(0, 0x4b); + + update_screen(); + wait(); + } + + wait_for(8); + set_hw_ink(0, 0x54); + draw_map(); +} + +void update_gtail() +{ + if ((*it_delay)++ == 2) + { + *it_delay = 0; + if (++(*it_frame) == 9) + { + *it_param ^= 128; + if (*it_param) + *it_frame = 2; + else + *it_frame = 0; + } + + if (*it_param) + *it_y -= gtail_y[*it_frame]; + else + *it_y += gtail_y[*it_frame]; + } + + if (!check_for_player(16)) + return; + + PLW_PlaySoundEffectP(EFX_MAGIC); + PLW_Init(songs, SONG_SPLIT); + + fill_screen(bgtiles[9]); + + // extra live + ++lives; + life = MAX_LIFE; + + gems = 30; + gtail = 1; + draw_hud_bg(); + draw_hud(); + wait(); + + *it_oy = *it_y; + + for (it_k = 0; it_k < 60; ++it_k) + { + if (it_k & 1) + { + --gems; + draw_hud(); + } + + *it_ox = *it_x; + *it_x ^= 2; + put_sprite4(items[0], *it_x, *it_y, 2, 0); + erase_sprite(*it_ox, *it_oy, 2); + + update_screen(); + wait(); + } + + + destroy_entity(); + update_persistence(sp_it->id); + + _gtail_effect(); + + PLW_Init(songs, SONG_INGAME); +} + +void update_pickup() +{ + if (!check_for_player(16)) + return; + + switch (sp_it->type) + { + case ET_POTION: + if (life == MAX_LIFE) + return; + life = MAX_LIFE; + break; + case ET_KEY: + ++keys; + break; + case ET_GEM: + ++gems; + break; + } + + erase_sprite(*it_x, *it_y, 2); + destroy_entity(); + + update_persistence(sp_it->id); + + PLW_PlaySoundEffectP(EFX_PICKUP); + + draw_hud(); +} diff --git a/src/et_pickups.h b/src/et_pickups.h new file mode 100644 index 0000000..1711833 --- /dev/null +++ b/src/et_pickups.h @@ -0,0 +1,28 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _PICKUPS_H +#define _PICKUPS_H + +void draw_pickup(); +void update_pickup(); + +void draw_gtail(); +void update_gtail(); + +#endif // _PICKUPS_H + diff --git a/src/et_platform.c b/src/et_platform.c new file mode 100644 index 0000000..0eab3cc --- /dev/null +++ b/src/et_platform.c @@ -0,0 +1,75 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "splib.h" +#include "main.h" + +#include "maps.h" + +// generated +#include "platform.h" + +#include "entities.h" +#include "et_player.h" +#include "et_platform.h" + +void draw_platform() { + put_sprite4(platform[*it_frame], *it_x, *it_y, 1, 0); + put_sprite4(platform[*it_frame], *it_x + 8, *it_y, 1, 0); + erase_sprite(*it_ox, *it_oy, 1); + erase_sprite(*it_ox + 8, *it_oy, 1); +} + +void update_platform() { + + // check limit and change direction + if ((*it_delay)++ >= *it_extra) + { + *it_delay = 1; + *it_param ^= 128; + } + + // track player if on the platform + if (check_for_point(sp_it, px + 1, py + 24, 16, 8) + || check_for_point(sp_it, px + 6, py + 24, 16, 8)) + { + py = *it_y - 24; + + if (*it_delay & 1) + return; + + if (*it_param) + { + if (!is_map_blocked(px + 8, py + (player_h ? 6 : 0)) + && !is_map_blocked(px + 8, py + 23)) + px += 2; + } + else + { + if (!is_map_blocked(px - 1, py + (player_h ? 6 : 0)) + && !is_map_blocked(px - 1, py + 23)) + px -= 2; + } + } + else if (*it_delay & 1) + return; + + if (*it_param) + *it_x += 2; + else + *it_x -= 2; +} diff --git a/src/et_platform.h b/src/et_platform.h new file mode 100644 index 0000000..b63961d --- /dev/null +++ b/src/et_platform.h @@ -0,0 +1,25 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _PLATFORM_H +#define _PLATFORM_H + +void draw_platform(); +void update_platform(); + +#endif // _PLATFORM_H + diff --git a/src/et_player.c b/src/et_player.c new file mode 100644 index 0000000..d58d656 --- /dev/null +++ b/src/et_player.c @@ -0,0 +1,551 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include <stdlib.h> + +#include "cpcrslib/cpcrslib.h" +#include "splib.h" +#include "plw.h" +#include "sound.h" +#include "main.h" + +#include "maps.h" +#include "stage.h" + +// generated +#include "player.h" +#include "explo.h" + +#include "entities.h" +#include "et_effect.h" + +#define LOCAL +#include "et_player.h" + +void player_checkpoint() +{ +#ifdef DEBUG + char b[4]; +#endif + + // don't do it if not solid or platform + if (is_map_blocked(px + 3, py + 24) == 1) + { + smap = cmap; + spx = px; + spy = py; + sdir = dir; + +#ifdef DEBUG + set_text_ink(15, 15, 15); + put_text("C", 10, 192); + pad_numbers(b, 3, px); + put_text(b, 13, 192); + pad_numbers(b, 3, py); + put_text(b, 20, 192); +#endif + } +} + +void player_hit(uint8_t h) +{ + life = life > h ? life - h : 0; + if (!life) + { + // dead + + // instant death cases control the frame + // otherwise set it here + if (h != MAX_LIFE) + frame = WALK + 1; + + erase_sprite(px, py, 3); + was_hit = 0; + respawn_delay = RESPAWN_DELAY; + + // not water + if (last_deadly != DEADLY_TILE_BASE) + PLW_PlaySoundEffectP(EFX_DEATH); + + if (lives == 1) + PLW_Init(songs, SONG_SILENCE); + } + else + { + // only hit + was_hit = WAS_HIT; + + if (*it_x > px) + dir = DIR_RIGHT; + else + dir = DIR_LEFT; + + PLW_PlaySoundEffectP(EFX_DAMAGE); + } + + draw_hud(); +} + +void draw_player() +{ + if ((!was_hit || was_hit & 2) && !magic && life) + put_sprite4(player[player_frames[frame]], px, py, 3, dir); + else if (magic) + put_sprite4(explo[2], px, py + 6, 2, magic & 1); + + if (!life && frame && respawn_delay > 2 && respawn_delay & 2) + put_sprite4(player[player_frames[frame]], px, py, 3, dir); + + erase_sprite(opx, opy, 3); + + opx = px; + opy = py; +} + +void update_player() +{ + // help is_map_blocked + sp_it = NULL; + moved = 0; + + if (respawn_delay) + { + if (--respawn_delay) + return; + + --lives; + life = lives ? MAX_LIFE : 0; + draw_hud(); + + if (!lives) + { + gameover_delay = GAMEOVER_DELAY; + return; + } + + if (cmap != smap) + init_map(smap); + px = spx; + py = spy; + dir = sdir; + frame = WALK; + player_h = 0; + was_hit = RESPAWN_DELAY; + return; + } + + if (!lives) + return; + + if (was_hit) + { + --was_hit; + + // jump after being hit + if (was_hit == WAS_HIT - 1) + { + jump_flag = 1; + player_h = 2; + } + + // jump back after being hit + if (was_hit > RESPAWN_DELAY) + { + moved = 1; + frame = WALK + 1; + + if (dir) + { + if (px < TW * TMW - 8 + && !is_map_blocked(px + 8, py + 23) + && !is_map_blocked(px + 8, py + (player_h ? 6 : 0))) + px += 2; + + if (px == TW * TMW - 8 && exit_map_right()) + return; + } + else + { + if (px > 0 + && !is_map_blocked(px - 1, py + 23) + && !is_map_blocked(px - 1, py + (player_h ? 6 : 0))) + px -= 2; + + if (!px && exit_map_left()) + return; + } + } + } + + // check for gravity + if (!player_h + && !is_map_blocked(px + 1, py + 24) + && !is_map_blocked(px + 6, py + 24)) + { + // start falling + player_h = JUMP_SEQ - 2; + frame = JUMP; + moved = 1; + } + + if (cpc_TestKeyF(KEY_DOWN) && frame != JUMP) + { + if (frame != CROUCH) + frame = CROUCH; + } + else + { + // wake up + if (frame == CROUCH) + frame = WALK; + } + + if (cpc_TestKeyF(KEY_UP)) + { + if (!player_h && !jump_flag + // no magic or magic just started + && (!magic || magic > COOL_DOWN - 2)) + { + jump_flag = 1; + player_h = 1; + frame = JUMP; + } + } + else + { + // faster than always set to 0 + if (jump_flag) + jump_flag = 0; + } + + if (cpc_TestKeyF(KEY_RIGHT) && !cpc_TestKeyF(KEY_LEFT)) + { + if (dir) + dir = DIR_RIGHT; + + if (frame != CROUCH) + { + moved = 1; + + if (px < TW * TMW - 8 + && !is_map_blocked(px + 8, py + 23) + && !is_map_blocked(px + 8, py + (player_h ? 6 : 0))) + px += 2; + + if (magic && px < TW * TMW - 8 + && !is_map_blocked(px + 8, py + 23) + && !is_map_blocked(px + 8, py + (player_h ? 6 : 0))) + px += 2; + + if (px == TW * TMW - 8 && exit_map_right()) + return; + } + } + + if (cpc_TestKeyF(KEY_LEFT) && !cpc_TestKeyF(KEY_RIGHT)) + { + if (!dir) + dir = DIR_LEFT; + + if (frame != CROUCH) + { + moved = 1; + + if (px > 0 + && !is_map_blocked(px - 1, py + 23) + && !is_map_blocked(px - 1, py + (player_h ? 6 : 0))) + px -= 2; + + if (magic && px > 0 + && !is_map_blocked(px - 1, py + 23) + && !is_map_blocked(px - 1, py + (player_h ? 6 : 0))) + px -= 2; + + if (!px && exit_map_left()) + return; + } + } + + if (magic) + { + if (magic-- == 2) + new_explo(); + + if (!magic + && (is_map_deadly(px + 3, py + 24) + || is_map_deadly(px + 3, py + (frame == CROUCH ? 6 : 0)) + )) + { + if (last_deadly == DEADLY_TILE_BASE) + { + frame = 0; + new_splash(); + } + else + frame = WALK + 1; + player_hit(MAX_LIFE); + return; + } + } + else + { + if (cool_down) + --cool_down; + + if (cpc_TestKeyF(KEY_FIRE) && !cool_down) + { + magic = COOL_DOWN; + cool_down = COOL_DOWN + 2; + new_explo(); + } + } + + if (player_h) + { + // jumping + it_k = jump_seq[player_h - 1]; + + if (IS_GOING_DOWN(player_h)) + { + // down + while (it_k && py < TH * TMH - 24) + { + // potentially hit floor + if ((is_map_blocked(px + 1, py + 24) + || is_map_blocked(px + 6, py + 24))) + { + // process deadly first + if (is_map_deadly(px + 1, py + 24) + || is_map_deadly(px + 6, py + 24)) + { + if (!magic) + { + if (last_deadly == DEADLY_TILE_BASE) + { + frame = 0; + new_splash(); + } + else + frame = WALK + 1; + player_hit(MAX_LIFE); + } + break; + } + + // avoid getting *inside* the platform + if (!is_map_blocked(px + 1, py + 22) + && !is_map_blocked(px + 6, py + 22)) + { + // hit floor + player_h = 0; + frame = WALK; + + player_checkpoint(); + + if (!magic) + PLW_PlaySoundEffectP(EFX_HIT); + break; + } + } + + py += 2; + it_k -= 2; + } + + if (py == TH * TMH - 24 && exit_map_down()) + return; + } + else + { + // up + while (it_k && py) + { + // jumping back after being hit + if (was_hit > RESPAWN_DELAY) + { + if (is_map_blocked(px + 1, py) + || is_map_blocked(px + 6, py)) + break; + } + + if (is_map_blocked(px + 1, py + 4) + || is_map_blocked(px + 6, py + 4)) + { + if (!magic) + { + if (is_map_deadly(px + 3, py + 4)) + { + frame = WALK + 1; + player_hit(MAX_LIFE); + } + else if (jump_flag == 1) + { + jump_flag = 2; + PLW_PlaySoundEffectP(EFX_HIT); + } + } + break; + } + + py -= 2; + it_k -= 2; + } + + if (py == 0 && exit_map_up()) + return; + } + + if (player_h && player_h < JUMP_SEQ + // extra boost with magic but not first step + && (!(magic & 1) || player_h == 1)) + player_h++; + } + else + { + // not jumping + if (moved) + { + if (!magic && (is_map_deadly(px + 3, py + 24) + || is_map_deadly(px + 3, py))) + { + if (last_deadly == DEADLY_TILE_BASE) + { + frame = 0; + new_splash(); + } + else + frame = WALK + 1; + player_hit(MAX_LIFE); + return; + } + + if (wdelay++) + { + wdelay = 0; + // frame increase + if (frame != JUMP && ++frame > WALK_CYCLE) + frame = WALK; + } + } + else + { + // frame stopped + if (frame > WALK && frame < CROUCH) + { + frame = WALK; + wdelay = 0; + } + } + } +} + +#pragma save +#pragma disable_warning 85 +#pragma disable_warning 59 +uint8_t check_for_player(uint8_t h) __z88dk_fastcall +{ + /* + if (magic || was_hit || !life) + return 0; + + if (frame == CROUCH) + { + if (py + 8 < sp_it->y + h && sp_it->y < py + 24 + && px + 2 < sp_it->x + w && sp_it->x < px + 6) + return 1; + } + else + { + if (py < sp_it->y + h && sp_it->y < py + 24 + && px + 2 < sp_it->x + w && sp_it->x < px + 6) + return 1; + } + + return 0; + */ + + // *INDENT-OFF* + __asm; + ld e, #8 ; w + ld d, l ; h + + ld a, (_magic) + or a + jr nz, check_for_player_false + ld a, (_was_hit) + or a + jr nz, check_for_player_false + ld a, (_life) + or a + jr z, check_for_player_false + + ld hl, (_sp_it) + ld bc, #6 + add hl, bc + ld a, (hl) ; y + add d + ld c, a + + ; b is 0 + ld a, (_frame) + cp #CROUCH + jr nz, not_crouching + + ld b, #8 + + not_crouching: + ld a, (_py) + add a, b + cp c + jr nc, check_for_player_false + + ld a, (_py) + add #24 + ld c, a + + ld a, (hl) ; y + cp c + jr nc, check_for_player_false + + dec hl + dec hl + ld a, (hl) ; x + add e + ld c, a + + ld a, (_px) + add #2 + cp c + jr nc, check_for_player_false + + ld a, (_px) + add #6 + ld c, a + + ld a, (hl) ; x + cp c + jr nc, check_for_player_false + + ld l, #1 + ret + + check_for_player_false: + ld l, #0 + __endasm; +// *INDENT-ON* +} +#pragma restore diff --git a/src/et_player.h b/src/et_player.h new file mode 100644 index 0000000..a72aa28 --- /dev/null +++ b/src/et_player.h @@ -0,0 +1,65 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _PLAYER_H +#define _PLAYER_H + +#include <stdint.h> + +#define WALK 0 +#define WALK_CYCLE 3 +#define CROUCH 4 +#define JUMP 5 + +#define DIR_RIGHT 0 +#define DIR_LEFT 1 +#define DIR_DOWN 2 +#define DIR_UP 3 + +#ifndef LOCAL +#define LOCAL extern +extern const uint8_t player_frames[]; +extern const uint8_t jump_seq[]; +#else +const uint8_t player_frames[6] = { 1, 0, 1, 2, 3, 3 }; +const uint8_t jump_seq[8] = { 14, 12, 4, 2, 0, 4, 8, 12 }; +#endif + +#define JUMP_SEQ 8 +#define IS_GOING_DOWN(x) ((x) > 5) + + +#define COOL_DOWN 10 +#define RESPAWN_DELAY 16 +#define WAS_HIT 24 +#define GAMEOVER_DELAY 48 + +#define MAX_LIFE 5 + +void player_checkpoint(); + +void draw_player(); +void update_player(); + +void player_hit(uint8_t h); + +uint8_t check_for_player(uint8_t h) __z88dk_fastcall; + +#undef LOCAL + +#endif // _PLAYER_H + diff --git a/src/et_spider.c b/src/et_spider.c new file mode 100644 index 0000000..8373d79 --- /dev/null +++ b/src/et_spider.c @@ -0,0 +1,102 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "splib.h" +#include "plw.h" +#include "sound.h" +#include "main.h" + +#include "maps.h" + +// generated +#include "spider.h" + +#include "entities.h" +#include "et_spider.h" +#include "et_player.h" + +void draw_spider() +{ + if (!*it_frame) + put_sprite4(spider[1], *it_x, *it_y, 1, 0); + else + { + // draws the thread + if (!(*it_param & 128)) + it_k = *it_param & 127; + else + it_k = *it_extra - (*it_param & 127); + + it_k >>= 3; + while (it_k) + { + put_sprite4(spider[2], *it_x, *it_y - (it_k << 3), 1, 0); + --it_k; + } + + put_sprite4(spider[0], *it_x, *it_y, 2, 0); + erase_sprite(*it_ox, *it_oy, 2); + } +} + +void update_spider() +{ + // waiting + if (!*it_frame) + { + if (!magic && life && py > *it_y && abs_sub(*it_x, px) < 9 && abs_sub(*it_y, py) < 48) + { + *it_frame = 1; + PLW_PlaySoundEffectP(EFX_SPIDER); + } + else + return; + } + + // limit and change direction + if ((*it_param & 127) == *it_extra) + { + // back up + if (*it_param & 128) + { + erase_sprite(*it_x, *it_y + 8, 1); + *it_frame = 0; + *it_param = 0; + goto spider_done; + } + else + // touch bottom + *it_param = 128; + } + + if (*it_param & 128) + { + *it_y -= 1; + *it_param += 1; + } + else + { + *it_y += 8; + *it_param += 8; + } + +spider_done: + + // only going down + if (!(*it_param & 128) && check_for_player(16)) + player_hit(1); +} diff --git a/src/et_spider.h b/src/et_spider.h new file mode 100644 index 0000000..d18dc21 --- /dev/null +++ b/src/et_spider.h @@ -0,0 +1,25 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _SPIDER_H +#define _SPIDER_H + +void draw_spider(); +void update_spider(); + +#endif // _SPIDER_H + diff --git a/src/et_spirit.c b/src/et_spirit.c new file mode 100644 index 0000000..ff2fac6 --- /dev/null +++ b/src/et_spirit.c @@ -0,0 +1,55 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "splib.h" +#include "main.h" + +// generated +#include "spirit.h" + +#include "entities.h" +#include "et_spirit.h" +#include "et_player.h" +#include "et_common.h" + +void draw_spirit() +{ + put_sprite4(spirit[player_frames[*it_frame]], *it_x, *it_y, 3, *it_param & 128); + erase_sprite(*it_ox, *it_oy, 3); +} + +void update_spirit() +{ + if ((*it_delay)++ == 2) + { + *it_delay = 0; + if (++(*it_frame) > WALK_CYCLE) + *it_frame = WALK; + } + + update_fixed_common(); + + if (check_for_player(24)) + { + player_hit(1); + it_k = *it_param & 128; + *it_param = *it_extra - (*it_param & 127); + *it_param |= it_k ^ 128; + *it_delay = 0; + *it_frame = WALK; + } +} diff --git a/src/et_spirit.h b/src/et_spirit.h new file mode 100644 index 0000000..5bb5852 --- /dev/null +++ b/src/et_spirit.h @@ -0,0 +1,25 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _SPIRIT_H +#define _SPIRIT_H + +void draw_spirit(); +void update_spirit(); + +#endif // _SPIRIT_H + diff --git a/src/et_switch.c b/src/et_switch.c new file mode 100644 index 0000000..543ba25 --- /dev/null +++ b/src/et_switch.c @@ -0,0 +1,103 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "cpcrslib/cpcrslib.h" +#include "splib.h" +#include "plw.h" +#include "sound.h" +#include "main.h" + +#include "maps.h" + +// generated +#include "switch.h" +#include "tiles.h" + +#include "entities.h" +#include "et_player.h" +#include "et_switch.h" + +void update_switch_door(uint8_t x, uint8_t y, uint8_t t) +{ + x >>= 3; + y >>= 3; + + for (it_k = 0; it_k < 4; ++it_k) + { + put_tile(bgtiles[t], get_tile_xy(x, y)); + set_map_tile(x, y++, t); + } + + put_tile(bgtiles[BLOCKED_OPEN_DOOR_TILE], get_tile_xy(x, y)); + set_map_tile(x, y++, BLOCKED_OPEN_DOOR); +} + +void draw_switch() +{ + if (is_invalid_tile2(get_tile_xy(*it_x >> 3, *it_y >> 3))) + put_sprite4(switch_sprite[0], *it_x, *it_y, 2, *it_frame & 1); +} + +void update_switch() +{ + if (*it_delay) + { + *it_delay -= 1; + + if (*it_delay & 1) + { + if (!(*it_frame & 254)) + { + *it_frame += 6; + if (*it_frame & 2) + PLW_PlaySoundEffectP(EFX_TICK); + } + else + *it_frame -= 2; + } + + if (!*it_delay) + { + erase_sprite(*it_x, *it_y, 2); + + if (!magic && life + && py < *it_extra + 32 && *it_extra < py + 24 + && px + 2 < *it_param + 8 && *it_param < px + 6) + { + frame = WALK + 1; + player_hit(MAX_LIFE); + } + + goto switch_toggle; + } + } + + if (!check_for_player(16) || (*it_frame & 1)) + return; + + if (!player_h && cpc_TestKeyF(KEY_DOWN)) + { + *it_delay = 64; + +switch_toggle: + *it_frame &= 1; + *it_frame ^= 1; + + update_switch_door(*it_param, *it_extra, *it_frame & 1 ? 42 : SWITCH_DOOR_TILE); + PLW_PlaySoundEffectP(EFX_DOOR); + } +} diff --git a/src/et_switch.h b/src/et_switch.h new file mode 100644 index 0000000..e15eedc --- /dev/null +++ b/src/et_switch.h @@ -0,0 +1,27 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _SWITCH_H +#define _SWITCH_H + +void draw_switch(); +void update_switch(); + +void update_switch_door(uint8_t x, uint8_t y, uint8_t t); + +#endif // _SWITCH_H + diff --git a/src/et_vampire.c b/src/et_vampire.c new file mode 100644 index 0000000..eb62699 --- /dev/null +++ b/src/et_vampire.c @@ -0,0 +1,83 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "splib.h" +#include "main.h" + +#include "maps.h" + +// generated +#include "vampire.h" + +#include "entities.h" +#include "et_vampire.h" +#include "et_player.h" + +void draw_vampire() +{ + put_sprite4(vampire[player_frames[*it_frame]], *it_x, *it_y, 2, 0); + erase_sprite(*it_ox, *it_oy, 2); +} + +void update_vampire() +{ + // waiting + if (*it_frame == 4) + { + if (!magic && py > *it_y && abs_sub(*it_x, px) < 16 && abs_sub(*it_y, py) < 48) + *it_frame = 0; + else + return; + } + + if ((*it_delay)++) + { + *it_delay = 0; + if (++(*it_frame) > WALK_CYCLE) + *it_frame = 0; + } + + // don't chase the player if dead + if (!life) + return; + + if (*it_x > px && !is_map_blocked(*it_x - 1, *it_y + 8)) + *it_x -= 1; + if (*it_x < px && !is_map_blocked(*it_x + 8, *it_y + 8)) + *it_x += 1; + + if (*it_extra) + { + --(*it_extra); + if (*it_y && !is_map_blocked(*it_x + 4, *it_y - 1)) + *it_y -= 1; + } + else + if (abs_sub(*it_x, px) < 64) + { + if (*it_y > py && !is_map_blocked(*it_x + 4, *it_y - 1)) + *it_y -= 2; + if (*it_y < py && !is_map_blocked(*it_x + 4, *it_y + 16)) + *it_y += 2; + } + + if (check_for_player(16)) + { + player_hit(1); + *it_extra = WAS_HIT << 1; + } +} diff --git a/src/et_vampire.h b/src/et_vampire.h new file mode 100644 index 0000000..ecdb975 --- /dev/null +++ b/src/et_vampire.h @@ -0,0 +1,25 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _VAMPIRE_H +#define _VAMPIRE_H + +void draw_vampire(); +void update_vampire(); + +#endif // _VAMPIRE_H + diff --git a/src/int.h b/src/int.h new file mode 100644 index 0000000..8b779b3 --- /dev/null +++ b/src/int.h @@ -0,0 +1,45 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _INT_H + +#include <stdint.h> + +extern uint8_t int6; +extern uint8_t tick; +extern uint8_t plw_frame; +extern uint8_t wframes; + +void wait(); +void wait_for(uint8_t t) __z88dk_fastcall; +void wait_int6(); +void setup_int(uint8_t frames) __z88dk_fastcall; + +// must be 0xabab +#define MAX_CLOCK 0x0e0e + +void pause_player(); +void resume_player(); + +void reset_clock(); +uint16_t get_clock(); + +#undef LOCAL + +#endif // _INT_H + + diff --git a/src/int.z80 b/src/int.z80 new file mode 100644 index 0000000..9290d72 --- /dev/null +++ b/src/int.z80 @@ -0,0 +1,262 @@ +;; +;; Kitsune's Curse +;; Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> +;; +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. +;; + +.globl _wait +.globl _wait_for +.globl _wait_int6 +.globl _setup_int +.globl _wait_vsync +.globl _wframes + +.globl _pause_player +.globl _resume_player + +.globl _reset_clock +.globl _get_clock + +.globl _paused + +MAX_CLOCK_BYTE = 0x0e + +_wait:: + ld hl, #_tick + + ld a, (_wframes) + ld c, a +wait_loop: + ld a, (hl) + cp c + jr nc, wait_done + halt + jr wait_loop + +wait_done: + xor a + ld (hl), a + ret + +_wait_for:: + ld b, l +wait_for_loop: + call _wait + djnz wait_for_loop + ret + +_wait_int6:: + ld a, (_int6) + or a + ret nz + halt + jr _wait_int6 + ret + +_setup_int:: + ld a, l + ld (_wframes), a + + xor a + ld (_int6), a + ld (_tick), a + ld (paused_player), a + + ld (_paused), a + call _reset_clock + + ; sync + call _wait_vsync + halt + halt + call _wait_vsync + + di + + ld ix, #0x0038 + ld hl, #isr1 + ld (ix), #0xc3 + ld 1(ix), l + ld 2(ix), h + im 1 + + ei + ret + +isr1: + ex af,af + push hl + + ld hl, #isr2 + ld (#0x39), hl + + ; int6 ends + xor a + ld (_int6), a + + ld a, (paused_player) + or a + jr nz, player_is_paused + + push ix + push iy + push bc + push de + + call _PLW_Play + + pop de + pop bc + pop iy + pop ix + +player_is_paused: + pop hl + ex af,af + ei + ret + +isr2: + exx + ex af,af + ld hl, #isr3 + ld (#0x39), hl + exx + ex af,af + ei + ret + +isr3: + exx + ex af,af + ld hl, #isr4 + ld (#0x39), hl + ex af,af + exx + ei + ret + +isr4: + exx + ex af,af + ld hl, #isr5 + ld (#0x39), hl + + ; update clock + ld a, (_paused) + or a + jr nz, clock_is_paused + ld a, (int_cnt) + inc a + cp #50 + jr nz, not_full_sec + + ld hl, (clock) + ld a, h + cp #(MAX_CLOCK_BYTE) + jr nz, inc_clock + cp l + jr z, clock_limit + +inc_clock: + inc hl + ld (clock), hl + +clock_limit: + xor a + +not_full_sec: + ld (int_cnt), a + +clock_is_paused: + ex af,af + exx + ei + ret + +isr5: + exx + ex af,af + ld hl, #isr6 + ld (#0x39), hl + + ; count a tick (50Hz) + ld a, (_tick) + inc a + ld (_tick), a + + ex af,af + exx + ei + ret + +isr6: + exx + ex af,af + ld hl, #isr1 + ld (#0x39), hl + + ; int6 starts + ld a, #1 + ld (_int6), a + + exx + ex af,af + ei + ret + +_pause_player:: + di + ld a, #1 + ld (paused_player), a + ei + call _wait_int6 + jp _PLW_Stop + +_resume_player:: + di + xor a + ld (paused_player), a + ei + ret + +_reset_clock:: + di + xor a + ld (int_cnt), a + ld (clock), a + ld (clock + 1), a + ei + ret + +_get_clock:: + ld hl, (clock) + ret + +_wframes: + .ds 1 + +_int6: + .ds 1 +_tick: + .ds 1 + +int_cnt: + .ds 1 +clock: + .ds 2 + +paused_player: + .ds 1 diff --git a/src/loader.s b/src/loader.s new file mode 100644 index 0000000..a41d3ba --- /dev/null +++ b/src/loader.s @@ -0,0 +1,252 @@ +;; +;; Kitsune's Curse +;; Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> +;; +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. +;; +;; +;; TAPE LOADER +;; + +.include "cpcfirm.inc" +.include "../build/loader.opt" + +loader: + +.ifeq DISK + ; loading from disk + + ld hl, (#0xbe7d) ; save current drive + ld a, (hl) + ld (#drive+1), a +.endif ; end disk code + + ld c, #0xff + ld hl, #start + call mc_start_program + +start: + call kl_rom_walk + +.ifeq DISK +drive: + ld a, #0 ; restore drive + ld hl, (#0xbe7d) + ld (hl), a +.endif ; end disk code + + ld bc, #0 ; set border + call scr_set_border + + ld bc, #0 ; bg color + xor a + call scr_set_ink + + xor a ; set mode 0 + call scr_set_mode + + ld a, #0xff + call cas_noisy ; disable tape texts + +.ifeq DISK + ; first file is the SCRX + call load_file + +.else ; tape code + + ld ix, #TMP_ADDR + ld de, #SCRX_SIZE + call turboload + +.endif ; end tape code + + ; setup the palette + ld hl, #TMP_ADDR + #4 + call set_palette + + ; border is already 0 + + ld hl, #TMP_ADDR + #0x14 ; compressed data + ld de, #0xc000 ; uncompress into the screen + call aplib_depack + + ; loader size limited to 1024 bytes! +LOADER_END = LOADER_ADDR + 1024 + +.ifeq DISK + + ld hl, #fname_end - #1 + inc (hl) + ; load the code + call load_file + +.else ; tape code + + ; change stripes + ld a, #20 + ld (#turbo_col + 1), a + + ; load the compressed app + ld ix, #LOADER_END + ld de, #APP_SIZE_PAK + call turboload + +.endif ; tape code ends + + di + ; disable the firmware + im 1 + ld hl, #0x38 + ld (hl), #0xfb + inc hl + ld (hl), #0xc9 + + ; put the stack as high as we can + ld sp, #0xc000 + ei + + ; disable upper/lower roms + ld bc, #0x7f8c + out (c), c + + ; relocate the compressed data + ld bc, #APP_SIZE_PAK + ld hl, #LOADER_END + #APP_SIZE_PAK + ld de, #LOADER_END + #APP_SIZE + #PAK_SLOP +data_relocation_loop: + ld a, (hl) + ld (de), a + dec hl + dec de + ld a, b + or c + dec bc + jr nz, data_relocation_loop + + ; uncompress after the loader + ld hl, #LOADER_END + #APP_SIZE - (#APP_SIZE_PAK - #PAK_SLOP) + ld de, #LOADER_END + call aplib_depack + + ; relocate app to its final address + ld bc, #APP_SIZE + ld hl, #LOADER_END + #APP_SIZE + ld de, #APP_ADDR + #APP_SIZE +relocation_loop: + ld a, (hl) + ld (de), a + dec hl + dec de + ld a, b + or c + dec bc + jr nz, relocation_loop + + ; jp to the app entry point + .db #0xc3 + .dw #APP_EP + +set_palette: + push hl + call wait_vsync + + halt + halt + + call wait_vsync + pop hl + + xor a +set_palette_loop: + push hl + push af + ld c, (hl) + ld b, c + call scr_set_ink + pop af + pop hl + inc hl + inc a + cp #0x10 + jr nz, set_palette_loop + + halt + halt + halt + halt + halt + halt + ret + +wait_vsync: + ld b, #0xf5 + keep_waiting: + in a, (c) + rra + jr nc, keep_waiting + ret + +.ifeq DISK + +load_file: + ld hl, #fname + ld b, #fname_end-#fname + + ld de, #0x400 ; temp mem (only used in tape mode) + call cas_in_open + + push de + pop hl + call cas_in_direct + + call cas_in_close + ret + +fname: + .str "MAIN.BI0" +fname_end: + +.else ; tape code + +turboload: + di + ex af, af' + push af + ex af, af' + exx + push de + push bc + push hl + exx + xor a + ld r, a + dec a + call _turboload + jp nc, 0 + exx + pop hl + pop bc + pop de + exx + ex af, af' + pop af + ex af, af' + ei + ret + +.include "turboload.s" +.endif ; end tape code + +.area _DATA + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..d344e9b --- /dev/null +++ b/src/main.c @@ -0,0 +1,754 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "aplib.h" +#include "cpcrslib/cpcrslib.h" +#include "plw.h" +#include "sound.h" +#include "splib.h" +#include "stage.h" +#include "maps.h" +#include "entities.h" + +// generated +#include "palette.h" +#include "menubg.h" +#include "player.h" +#include "items.h" +#include "tiles.h" +#include "songs_pak.h" + +#include "et_effect.h" +#include "et_pickups.h" +#include "et_player.h" +#include "et_door.h" +#include "et_platform.h" +#include "et_switch.h" +#include "et_spirit.h" +#include "et_flame.h" +#include "et_vampire.h" +#include "et_oni.h" +#include "et_ninja.h" +#include "et_spider.h" +#include "et_demon.h" +#include "et_cloud.h" + +#define LOCAL +#include "main.h" + +#include "int.h" + +#define MAX_KEYS 6 + +uint8_t joystick; +const uint16_t key_map[2][MAX_KEYS] = { + // right, left, up, down, fire, pause + { 0x4002, 0x4101, 0x4001, 0x4004, 0x4580, 0x4308 }, // keyboard + { 0x4908, 0x4904, 0x4901, 0x4902, 0x4910, 0x4308 } // joystick +}; +const char redefine[MAX_KEYS][6] = { + "RIGHT", "LEFT ", "UP ", "DOWN ", "FIRE ", "PAUSE" +}; + +static uint8_t *buffer; + +void _pak_song(uint8_t n) +{ + buffer = (uint8_t *)BUFF_ADDR; + aplib_uncompress(buffer, songs_pak); + PLW_Init(buffer, n); +} + +void _menu_bg() +{ + buffer = (uint8_t *)get_tile_xy(0, 0); + aplib_uncompress(buffer, menubg); + cpc_PutSp(buffer, 50, 44, SCREEN_ADDR(18, 0)); +} + +void screen_black() +{ + /* + uint8_t i; + + wait_int6(); + + // all black + for (i = 0; i < 16; i++) + set_hw_ink(i, 0x54); + */ + + // *INDENT-OFF* + __asm; + + call _wait_int6 + + ld hl, #0x540f + +screen_black_loop: + push hl + push hl + call _set_hw_ink + pop af + pop hl + dec l + jr nz, screen_black_loop + + __endasm; + // *INDENT-ON* +} + +void set_colors() +{ + uint8_t c; + + wait_int6(); + + for (c = 0; c < 16; c++) + set_hw_ink(c, pal_hw[c]); + + // black + set_hw_border(0x54); +} + + +const uint8_t fade_in_c[] = { 0, 1, 8, 12, 14 }; + +void fade_in() +{ + uint8_t c, i; + + for (i = 0; i < 5; i++) + { + wait_int6(); + for (c = 1; c < 16; c++) + set_hw_ink(c, pal_hw[fade_in_c[i]]); + wait(); + } + + set_colors(); +} + +void draw_controls() +{ + if (joystick) + set_text_ink(15, 14, 11); + else + set_text_ink(13, 8, 2); + put_text("1 JOYSTICK", 30, 67); + + if (joystick) + set_text_ink(13, 8, 2); + else + set_text_ink(15, 14, 11); + put_text("2 KEYBOARD", 30, 77); + + set_text_ink(13, 8, 2); + put_text("3 REDEFINE", 30, 87); +} + +void map_keys() +{ + uint8_t i; + + for (i = 0; i < MAX_KEYS; i++) + cpc_AssignKey(i, key_map[joystick][i]); +} + +void run_redefine() +{ + uint8_t i; + + // we need more space than is available in the main buffer, so use the + // st_tile array that is not used here and is ~2800 bytes currently + buffer = (uint8_t *)get_tile_xy(0, 0); + + // clear the area + memset(buffer, 0, 30 * 80); + + wait_int6(); + + cpc_PutSp(buffer, 30, 80, SCREEN_ADDR(0, 65)); + cpc_PutSp(buffer, 10, 80, SCREEN_ADDR(0, 110)); + + // be sure the keyboard is free + while (cpc_AnyKeyPressed()) + wait(); + + set_text_ink(13, 8, 2); + put_text("PRESS KEY FOR:", 20, 85); + + set_text_ink(15, 15, 14); + + // clean existing keys + for (i = 0; i < MAX_KEYS; i++) + cpc_AssignKey(i, (int)0xffff); + + for (i = 0; i < MAX_KEYS; i++) + { + wait_int6(); + put_text(redefine[i], 50, 85); + cpc_RedefineKey(i); + } + + // clear the area + cpc_PutSp(buffer, 10, 80, SCREEN_ADDR(0, 85)); + + // be sure the keyboard is free + while (cpc_AnyKeyPressed()) + wait(); +} + +void run_intro() +{ + + screen_black(); + clear_screen(); + + set_text_ink(3, 5, 11); + put_text("WHAT IS YOUR TRUE NATURE, KITSUNE?", 6, 90); + + fade_in(); + + buffer = (uint8_t *)BUFF_ADDR; + aplib_uncompress(buffer, songs_pak); + PLW_Init(buffer, SONG_INTRO); + wait_for(82); +} + +void run_gameover() +{ + PLW_Init(songs, SONG_SILENCE); + + screen_black(); + clear_screen(); + set_colors(); + + set_text_ink(3, 5, 11); + wait_int6(); + put_text("GAME", 30, 90); + put_text("OVER", 42, 90); + + if (gtail) + { + cpc_PutMaskSp(items[0], 16, 4, SCREEN_ADDR(35, 102)); + // we can use wmap as buffer, the map is not in use + wmap[0] = '*'; + pad_numbers(wmap + 1, 2, gems); + put_text(wmap, 38, 105); + } + + _pak_song(SONG_GAMEOVER); + wait_for(112); +} + +const uint8_t * const ranks[5] = { + "SAGE DRAGON", + "BRAVE TIGER", + "SILENT SNAKE", + "DRUNK MONKEY", + "LITTLE CRICKET" +}; + +void run_endgame() +{ + uint16_t clock = get_clock(); + uint8_t m, s; + + PLW_Init(songs, SONG_SILENCE); + + screen_black(); + clear_screen(); + + _menu_bg(); + + init_tiles(); + validate_screen(); + put_tile(bgtiles[48], get_tile_xy(0, 10)); + put_tile(bgtiles[49], get_tile_xy(0, 11)); + put_tile(bgtiles[50], get_tile_xy(0, 12)); + update_screen(); + + m = clock / 60; + s = clock - m * 60; + + set_text_ink(15, 14, 11); + put_text("CONGRATULATIONS!", 6, 70); + + set_text_ink(15, 13, 8); + put_text("THE CURSE IS BROKEN AND KITSUNE HAS", 6, 85); + put_text("NOW THE GOLDEN TAIL. HAPPY ENDING!", 6, 95); + + set_text_ink(5, 3, 4); + put_text("TIME:", 6, 118); + put_text("KY[:", 8, 128); + + // check max range + if (clock == MAX_CLOCK) + { + set_text_ink(6, 5, 3); + put_text("TOO SLOW!", 18, 118); + } + else + { + set_text_ink(13, 8, 2); + // we can use wmap as buffer, the map is not in use + pad_numbers(wmap, 2, m); + put_text(wmap, 18, 118); + wmap[0] = ':'; + pad_numbers(wmap + 1, 2, s); + put_text(wmap, 22, 118); + } + + for (it_k = 0, s = 25; it_k < 4; ++it_k) + { + if (m < s) + break; + s += 5; + } + put_text(ranks[it_k], 18, 128); + + fade_in(); + + _pak_song(SONG_MENU); + + wait_for(0); + wait_for(0); + wait_for(249); +} + +void draw_menu() +{ + screen_black(); + clear_screen(); + + _menu_bg(); + + draw_controls(); + + set_text_ink(5, 3, 4); + put_text("CODE, GRAPHICS & SOUND", 18, 135); + + set_text_ink(15, 6, 5); + put_text("JUAN J. MARTINEZ", 24, 145); + +#ifdef DEBUG + set_text_ink(15, 15, 15); + put_text(VERSION, 0, 175); +#endif + + set_text_ink(15, 13, 8); + put_text("\x1f""2020 USEBOX.NET", 24, 175); + + fade_in(); +} + +void draw_hud_bg() +{ + // lives + cpc_PutMaskSp(player[1] + 8, 10, 4, SCREEN_ADDR(2, 10)); + + // keys + cpc_PutMaskSp(items[1], 16, 4, SCREEN_ADDR(59, 8)); + + // gems + if (gtail) + cpc_PutMaskSp(items[0], 16, 4, SCREEN_ADDR(68, 8)); + + set_text_ink(14, 11, 3); + put_text("KITSUNE'S", 25, 12); + put_text("CURSE", 45, 12); +} + +void draw_hud() +{ + uint8_t i; + char b[4]; + + wait_int6(); + + b[0] = '='; + b[1] = 0; + set_text_ink(6, 5, 3); + for (i = 0; i < MAX_LIFE; i++) + { + if (life == i) + set_text_ink(2, 1, 1); + + put_text(b, 6 + i * 2, 12); + } + + set_text_ink(14, 12, 7); + b[0] = '*'; + pad_numbers(b + 1, 1, lives); + put_text(b, 16, 12); + + pad_numbers(b + 1, 1, keys); + put_text(b, 62, 12); + + if (gtail) + { + pad_numbers(b + 1, 2, gems); + put_text(b, 71, 12); + } +} + +void init_persistence() +{ + memset(&jump_flag, 0, ZERO_ALL); + memset(persistence, 0, PERSISTENCE_LEN); + + lives = 3; + life = MAX_LIFE; + opx = px = START_X; + opy = py = START_Y; + dir = DIR_RIGHT; + frame = WALK; + + init_tiles(); + init_map(START_MAP); + + // after setting cmap + player_checkpoint(); +} + +void update_persistence(uint8_t id) +{ + persistence[id >> 3] |= (1 << (id & 7)); +} + +uint8_t check_persistence(uint8_t id) +{ + return (persistence[id >> 3] & (1 << (id & 7))); +} + +void fill_map_row(const uint8_t *ent) +{ + uint8_t t, x, y, w; + + t = ent[1]; + x = ent[2] >> 3; + y = ent[3] >> 3; + w = ent[4]; + + while (w--) + set_map_tile(x++, y, t); +} + +// init table: update, draw +static void (* const init[])() = { + update_door, draw_door, + update_platform, draw_platform, + update_spirit, draw_spirit, + update_flame, draw_flame, + update_vampire, draw_vampire, + update_oni, draw_oni, + update_ninja, draw_ninja, + update_spider, draw_spider, + update_demon, draw_demon, + update_cloud, draw_cloud, + update_tile_anim, draw_torch, + update_switch, draw_switch, + update_pickup, draw_pickup, + update_pickup, draw_pickup, + update_pickup, draw_pickup, + update_gtail, draw_gtail, +}; + +void spawn_entities(const uint8_t *ents) +{ + uint8_t typ; + + // ents CAN'T BE NULL + while (*ents != 0xff) + { + // not really entities + if (*ents == ET_FILL) + { + fill_map_row(ents); + ents += 1; + goto next_et; + } + if ((*ents & 127) == ET_LINK) + { + // down + if (*ents & 128) + { + exit_down = ents[1]; + offset_down = ents[2]; + } + else + { + exit_up = ents[1]; + offset_up = ents[2]; + } + goto next_et; + } + + if (ents[1] != 255) + { + if (check_persistence(ents[1]) + || (!gtail && *ents == ET_GEM)) + goto next_et; + } + + if (!new_entity()) + // unlikely + return; + + // init to 0, skip *n + memset(sp_new, 0, sizeof(struct st_entity) - 2); + + typ = *ents & 127; + + sp_new->type = typ; + sp_new->param = ents[0] & 128; + sp_new->id = ents[1]; + sp_new->x = sp_new->ox = ents[2]; + sp_new->y = sp_new->oy = ents[3]; + + memcpy(&(sp_new->update), &init[(typ - ET_FIRST) << 1], 4); + + switch (sp_new->type) + { + case ET_DOOR: + if (sp_new->param) + sp_new->frame = 1; + break; + case ET_SPIDER: + sp_new->param = 0; + sp_new->extra = ents[4] - TH; + ++ents; + break; + case ET_SPIRIT: + case ET_FLAME: + // distance + sp_new->extra = ents[4]; + ++ents; + break; + case ET_VAMPIRE: + // start waiting + sp_new->frame = 4; + break; + case ET_ONI: + // speed + sp_new->extra = 2; + break; + case ET_DEMON: + // fire early + sp_new->extra = 64; + break; + case ET_PLATFORM: + if (ts == (1 << 4)) + sp_new->frame = 1; + sp_new->extra = ents[4]; + ++ents; + break; + case ET_TORCH: + // x and y expected unit is tiles + sp_new->x = ents[2] >> 3; + sp_new->y = ents[3] >> 3; + break; + case ET_SWITCH: + // x, y of the door; in tiles + sp_new->param = ents[4]; + sp_new->extra = ents[5]; + update_switch_door(ents[4], ents[5], SWITCH_DOOR_TILE); + ents += 2; + break; + } + +next_et: + ents += 4; + } +} + +void run_game() +{ + PLW_Init(songs, SONG_SILENCE); + + screen_black(); + clear_screen(); + + init_persistence(); + + draw_entities(); + update_screen(); + + draw_hud_bg(); + draw_hud(); + + set_colors(); + + reset_clock(); + + PLW_Init(songs, SONG_INGAME); + + while(1) + { + cpc_ScanKeyboard(); + + if (cpc_TestKeyF(KEY_PAUSE)) + { + if (paused) + { + invalidate_screen(); + draw_entities(); + update_screen(); + paused = 0; + resume_player(); + } + else + { + set_text_ink(15, 15, 13); + wait_int6(); + put_text("GAME", 28, 85); + put_text("PAUSED", 40, 85); + paused = 1; + pause_player(); + PLW_InitSoundEffects(effects); + } + + // be sure the keyboard is free + while (cpc_AnyKeyPressed()) + wait(); + } + + if (paused) + continue; + + if (cpc_TestKeyF(KEY_QUIT)) + { + PLW_Init(songs, SONG_SILENCE); + break; + } + + if (gtail && gems == MAX_GEMS && !gameover_delay) + { + run_endgame(); + run_gameover(); + return; + } + + if (gameover_delay) + { + gameover_delay--; + if (!gameover_delay) + { + run_gameover(); + return; + } + } + + update_entities(); + draw_entities(); + + wait(); + update_screen(); + } +} + +void main(void) +{ + uint8_t delay = 0, kdelay = 1; + + // before setting up the int! + PLW_InitSoundEffects(effects); + PLW_Init(songs, SONG_SILENCE); + + // init with wait at 16.6 FPS + setup_int(3); + + set_colors(); + + run_intro(); + + // defaults to joystick + joystick = 1; + map_keys(); + cpc_AssignKey(KEY_QUIT, 0x4804); // ESC + cpc_AssignKey(8, 0x4801); // 1 + cpc_AssignKey(9, 0x4802); // 2 + cpc_AssignKey(10, 0x4702); // 3 + +back_to_menu: + + draw_menu(); + + _pak_song(SONG_MENU); + + while (1) + { + cpc_ScanKeyboard(); + + if (!cpc_AnyKeyPressed()) + kdelay = 0; + + if (!kdelay) + { + if ((cpc_TestKeyF(KEY_1) && !joystick) + || (cpc_TestKeyF(KEY_2) && joystick)) + { + joystick = !joystick; + draw_controls(); + + kdelay = 1; + + map_keys(); + continue; + } + + if (cpc_TestKeyF(KEY_3) && !cpc_TestKeyF(8)) + { + joystick = 0; + + run_redefine(); + + draw_controls(); + continue; + } + + if (cpc_TestKey(KEY_FIRE)) + { + run_game(); + + kdelay = 1; + goto back_to_menu; + } + } + + delay += 0x10; + if (delay == 0x30) + { + set_text_ink(14, 12, 7); + goto fire_to_play; + } + if (!delay) + { + set_text_ink(0, 0, 0); +fire_to_play: + wait_int6(); + put_text("FIRE TO PLAY", 28, 110); + } + + wait(); + } +} diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..7d367c8 --- /dev/null +++ b/src/main.h @@ -0,0 +1,63 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _MAIN_H +#define _MAIN_H + +//#define DEBUG +#define VERSION "B5" + +#ifndef LOCAL +#define LOCAL extern +#endif + +#define KEY_RIGHT 0 +#define KEY_LEFT 1 +#define KEY_UP 2 +#define KEY_DOWN 3 +#define KEY_FIRE 4 +#define KEY_PAUSE 5 +#define KEY_1 8 +#define KEY_2 9 +#define KEY_3 10 +#define KEY_QUIT 11 + +#define MAX_GEMS 30 + +void draw_hud(); +void draw_hud_bg(); + +void init_persistence(); +void update_persistence(uint8_t id); +uint8_t check_persistence(uint8_t id); +void spawn_entities(const uint8_t *ents); + +void screen_black(); +void set_colors(); + +LOCAL uint8_t frame, px, py, opx, opy, spx, spy, smap, sdir, dir; +LOCAL uint8_t moved; + +// all these will be zeroed in one go +LOCAL uint8_t jump_flag, player_h, magic, wdelay, paused, was_hit, cool_down, respawn_delay, gameover_delay, keys, gems, gtail; +#define ZERO_ALL 12 + +LOCAL uint8_t lives, life; + +#undef LOCAL + +#endif // _MAIN_H diff --git a/src/maps.c b/src/maps.c new file mode 100644 index 0000000..87928b2 --- /dev/null +++ b/src/maps.c @@ -0,0 +1,239 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include <stdint.h> + +#include "aplib.h" +#include "main.h" +#include "splib.h" + +#include "tiles.h" + +#define LOCAL +#include "maps.h" +#include "stage.h" + +#include "entities.h" + +// working map (using 4-bit per tile), 8x8 +static uint8_t ml; +static const unsigned char *map_ents; +static uint8_t blocked; + +void _expand_map() +{ + uint8_t *b = (uint8_t *)BUFF_ADDR; + uint16_t i; + + // uncompress at the end of the working area + aplib_uncompress(b, (uint8_t *)(map[cmap] + 2)); + + // expand from 4-bpt to 8-bpt + for (i = 0; i < TMW * TMH; i += 2) + { + wmap[i] = (*b >> 4) + ts; + wmap[i + 1] = (*b & 0x0f) + ts; + b++; + } +} + +void draw_map() +{ + uint8_t i, j, k, c, m; + + validate_screen(); + + // draw the bottom to top; invalidate is LIFO + for (j = TMH; j; --j) + { + k = j - 1; + for (i = 0; i < TMW; ++i) + { + c = wmap[i + k * TMW]; + + // only if is part of a tileset + if (c < LAST_TILE_TILESET) + { + m = c & 15; + + // special tiles: bricks + if (!m || m == 12) { + if (k & 1) + ++c; + } + } else if (c == BLOCKED_OPEN_DOOR) + c = BLOCKED_OPEN_DOOR_TILE; + + put_tile(bgtiles[c], get_tile_xy(i, k)); + } + } +} + +void init_map(uint8_t m) +{ +#ifdef DEBUG + pad_numbers(wmap, 2, m); + set_text_ink(15, 15, 15); + put_text(wmap, 0, 192); +#endif + + cmap = m; + // map length (not including tileset or flags) + ml = map[cmap][0]; + // tileset + ts = (map[cmap][1] & 0x0f) << 4; + blocked = map[cmap][1] & 0xf0; + // map_ents + map_ents = map[cmap] + 2 + ml; + + _expand_map(); + + exit_up = cmap - WMAPS; + offset_up = 0; + exit_down = cmap + WMAPS; + offset_down = 0; + + init_entities(); + spawn_entities(map_ents); + + draw_map(); +} + +void set_map(uint8_t m) +{ + init_map(m); + + if (!player_h) + { + // IMPORTANT: set new player coords before + // calling to set_map! + + // checkpoint + smap = cmap; + spx = px; + spy = py; + sdir = dir; + } + + draw_entities(); + update_screen(); +} + +void set_map_tile(uint8_t x, uint8_t y, uint8_t tile) +{ + wmap[x + (y * TMW)] = tile; +} + +static struct st_entity *sp_mit; +static uint8_t group, last_blocked; + +uint8_t is_map_blocked(uint8_t x, uint8_t y) +{ + last_blocked = wmap[(x >> 3) + ((y >> 3) * TMW)]; + + if (last_blocked == BLOCKED_OPEN_DOOR) + return BLOCKED_OPEN_DOOR; + + if (last_blocked < LAST_TILE_TILESET) + { + if((last_blocked & 15) > LAST_NON_SOLID) + return 1; + } + else + // from this on, all solids + if (last_blocked > SWITCH_DOOR_TILE - 1) + return 1; + + group = 0; + for (sp_mit = sp_used; sp_mit; sp_mit = sp_mit->n) + { + // this requires ET_DOOR and ET_PLATFORM to go grouped + switch (sp_mit->type) + { + case ET_PLATFORM: + group = 1; + if (check_for_point(sp_mit, x, y, 16, 8)) + return BLOCKED_PLATFORM; + break; + + case ET_DOOR: + group = 1; + if (check_for_point(sp_mit, x, y, 8, 32) && (sp_it || !keys || magic)) + return 1; + break; + + default: + if (group) + return 0; + break; + } + } + + return 0; +} + +uint8_t is_map_deadly(uint8_t x, uint8_t y) +{ + last_deadly = wmap[(x >> 3) + ((y >> 3) * TMW)]; + + // deadly tiles are at the end of the tile table + return (last_deadly < 64 && last_deadly > DEADLY_TILE_BASE - 1); +} + +uint8_t exit_map_left() +{ + if (blocked & BLOCKED_LEFT) + return 0; + + opx = px = (uint8_t)(TW * TMW - 8); + set_map(cmap - 1); + return 1; +} + +uint8_t exit_map_right() +{ + if (blocked & BLOCKED_RIGHT) + return 0; + + opx = px = 0; + set_map(cmap + 1); + return 1; +} + +uint8_t exit_map_up() +{ + if (blocked & BLOCKED_UP) + return 0; + + px += offset_up; + opx = px; + opy = py = (uint8_t)(TH * TMH - 24); + set_map(exit_up); + return 1; +} + +uint8_t exit_map_down() +{ + if (blocked & BLOCKED_DOWN) + return 0; + + px += offset_down; + opx = px; + opy = py = 0; + set_map(exit_down); + return 1; +} diff --git a/src/maps.h b/src/maps.h new file mode 100644 index 0000000..8a3dad1 --- /dev/null +++ b/src/maps.h @@ -0,0 +1,66 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _MAPS_H +#define _MAPS_H + +#include <stdint.h> + +#ifndef LOCAL +#define LOCAL extern +#endif + +void init_map(uint8_t m); +void set_map(uint8_t m); +void draw_map(); + +uint8_t exit_map_left(); +uint8_t exit_map_right(); +uint8_t exit_map_up(); +uint8_t exit_map_down(); + +void set_map_tile(uint8_t x, uint8_t y, uint8_t tile); +uint8_t is_map_blocked(uint8_t x, uint8_t y); +uint8_t is_map_deadly(uint8_t x, uint8_t y); + +LOCAL uint8_t cmap, ts; +LOCAL uint8_t exit_up, offset_up, exit_down, offset_down; + +LOCAL uint8_t last_deadly; + +LOCAL uint8_t wmap[TMW * TMH]; + +// highest bit denotes deadly tile +#define DEADLY_TILE_BASE 59 + +#define BLOCKED_PLATFORM 128 +#define BLOCKED_OPEN_DOOR 143 +#define BLOCKED_OPEN_DOOR_TILE 43 + +#define SWITCH_DOOR_TILE 58 + +#define BLOCKED_UP (1<<4) +#define BLOCKED_DOWN (1<<5) +#define BLOCKED_LEFT (1<<6) +#define BLOCKED_RIGHT (1<<7) + +#define LAST_TILE_TILESET 48 +#define LAST_NON_SOLID 10 + +#undef LOCAL + +#endif // _MAPS_H diff --git a/src/songs.z80 b/src/songs.z80 new file mode 100644 index 0000000..1d14dcd --- /dev/null +++ b/src/songs.z80 @@ -0,0 +1,1421 @@ +; Song Kitsune's Curse in Lightweight format (V1).
+; Generated by Arkos Tracker 2.
+
+KitsunesCurse_Start:
+KitsunesCurse_StartDisarkGenerateExternalLabel:
+
+KitsunesCurse_DisarkByteRegionStart0:
+ .str "ATLW" ; Format marker (LightWeight).
+ .db 1 ; Format version.
+KitsunesCurse_DisarkByteRegionEnd0:
+KitsunesCurse_DisarkPointerRegionStart1:
+ .dw KitsunesCurse_FmInstrumentTable
+ .dw KitsunesCurse_ArpeggioTable
+ .dw KitsunesCurse_PitchTable
+; Table of the Subsongs.
+ .dw KitsunesCurse_Subsong0
+ .dw KitsunesCurse_Subsong1
+ .dw KitsunesCurse_Subsong2
+KitsunesCurse_DisarkPointerRegionEnd1:
+
+; The Arpeggio table.
+KitsunesCurse_ArpeggioTable:
+KitsunesCurse_DisarkWordRegionStart2:
+ .dw 0
+KitsunesCurse_DisarkWordRegionEnd2:
+KitsunesCurse_DisarkPointerRegionStart3:
+KitsunesCurse_DisarkPointerRegionEnd3:
+
+; The Pitch table.
+KitsunesCurse_PitchTable:
+KitsunesCurse_DisarkWordRegionStart4:
+ .dw 0
+KitsunesCurse_DisarkWordRegionEnd4:
+KitsunesCurse_DisarkPointerRegionStart5:
+KitsunesCurse_DisarkPointerRegionEnd5:
+
+; The FM Instrument table.
+KitsunesCurse_FmInstrumentTable:
+KitsunesCurse_DisarkPointerRegionStart6:
+ .dw KitsunesCurse_FmInstrument0
+ .dw KitsunesCurse_FmInstrument1
+ .dw KitsunesCurse_FmInstrument2
+ .dw KitsunesCurse_FmInstrument3
+ .dw KitsunesCurse_FmInstrument4
+ .dw KitsunesCurse_FmInstrument5
+ .dw KitsunesCurse_FmInstrument6
+KitsunesCurse_DisarkPointerRegionEnd6:
+
+KitsunesCurse_DisarkByteRegionStart7:
+KitsunesCurse_FmInstrument0:
+ .db 255 ; Speed.
+
+KitsunesCurse_FmInstrument0Loop: .db 0 ; Volume: 0.
+
+ .db 4 ; End the instrument.
+KitsunesCurse_DisarkPointerRegionStart8:
+ .dw KitsunesCurse_FmInstrument0Loop ; Loops.
+KitsunesCurse_DisarkPointerRegionEnd8:
+
+KitsunesCurse_FmInstrument1:
+ .db 0 ; Speed.
+
+ .db 101 ; Volume: 9.
+ .dw 2 ; Pitch: 2.
+
+ .db 37 ; Volume: 9.
+
+ .db 97 ; Volume: 8.
+ .dw -2 ; Pitch: -2.
+
+ .db 33 ; Volume: 8.
+
+ .db 29 ; Volume: 7.
+
+ .db 25 ; Volume: 6.
+
+ .db 21 ; Volume: 5.
+
+ .db 17 ; Volume: 4.
+
+ .db 13 ; Volume: 3.
+
+ .db 4 ; End the instrument.
+KitsunesCurse_DisarkPointerRegionStart9:
+ .dw KitsunesCurse_FmInstrument0Loop ; Loop to silence.
+KitsunesCurse_DisarkPointerRegionEnd9:
+
+KitsunesCurse_FmInstrument2:
+ .db 0 ; Speed.
+
+ .db 41 ; Volume: 10.
+
+ .db 41 ; Volume: 10.
+
+ .db 41 ; Volume: 10.
+
+ .db 25 ; Volume: 6.
+
+ .db 25 ; Volume: 6.
+
+ .db 25 ; Volume: 6.
+
+ .db 4 ; End the instrument.
+KitsunesCurse_DisarkPointerRegionStart10:
+ .dw KitsunesCurse_FmInstrument0Loop ; Loop to silence.
+KitsunesCurse_DisarkPointerRegionEnd10:
+
+KitsunesCurse_FmInstrument3:
+ .db 0 ; Speed.
+
+ .db 173 ; Volume: 11.
+ .db 1 ; Arpeggio: 0.
+ .db 15 ; Noise: 15.
+
+ .db 169 ; Volume: 10.
+ .db 233 ; Arpeggio: -12.
+ .db 15 ; Noise: 15.
+
+ .db 165 ; Volume: 9.
+ .db 1 ; Arpeggio: 0.
+ .db 15 ; Noise: 15.
+
+ .db 161 ; Volume: 8.
+ .db 232 ; Arpeggio: -12.
+
+ .db 4 ; End the instrument.
+KitsunesCurse_DisarkPointerRegionStart11:
+ .dw KitsunesCurse_FmInstrument0Loop ; Loop to silence.
+KitsunesCurse_DisarkPointerRegionEnd11:
+
+KitsunesCurse_FmInstrument4:
+ .db 0 ; Speed.
+
+ .db 173 ; Volume: 11.
+ .db 232 ; Arpeggio: -12.
+
+ .db 41 ; Volume: 10.
+
+ .db 165 ; Volume: 9.
+ .db 24 ; Arpeggio: 12.
+
+ .db 33 ; Volume: 8.
+
+ .db 4 ; End the instrument.
+KitsunesCurse_DisarkPointerRegionStart12:
+ .dw KitsunesCurse_FmInstrument0Loop ; Loop to silence.
+KitsunesCurse_DisarkPointerRegionEnd12:
+
+KitsunesCurse_FmInstrument5:
+ .db 0 ; Speed.
+
+ .db 41 ; Volume: 10.
+
+ .db 41 ; Volume: 10.
+
+ .db 41 ; Volume: 10.
+
+ .db 41 ; Volume: 10.
+
+KitsunesCurse_FmInstrument5Loop: .db 97 ; Volume: 8.
+ .dw 4 ; Pitch: 4.
+
+ .db 33 ; Volume: 8.
+
+ .db 97 ; Volume: 8.
+ .dw -4 ; Pitch: -4.
+
+ .db 33 ; Volume: 8.
+
+ .db 4 ; End the instrument.
+KitsunesCurse_DisarkPointerRegionStart13:
+ .dw KitsunesCurse_FmInstrument5Loop ; Loops.
+KitsunesCurse_DisarkPointerRegionEnd13:
+
+KitsunesCurse_FmInstrument6:
+ .db 0 ; Speed.
+
+ .db 165 ; Volume: 9.
+ .db 24 ; Arpeggio: 12.
+
+ .db 37 ; Volume: 9.
+
+ .db 165 ; Volume: 9.
+ .db 232 ; Arpeggio: -12.
+
+ .db 37 ; Volume: 9.
+
+ .db 165 ; Volume: 9.
+ .db 24 ; Arpeggio: 12.
+
+ .db 25 ; Volume: 6.
+
+ .db 153 ; Volume: 6.
+ .db 232 ; Arpeggio: -12.
+
+ .db 25 ; Volume: 6.
+
+ .db 145 ; Volume: 4.
+ .db 24 ; Arpeggio: 12.
+
+ .db 17 ; Volume: 4.
+
+ .db 145 ; Volume: 4.
+ .db 232 ; Arpeggio: -12.
+
+ .db 17 ; Volume: 4.
+
+ .db 137 ; Volume: 2.
+ .db 24 ; Arpeggio: 12.
+
+ .db 9 ; Volume: 2.
+
+ .db 137 ; Volume: 2.
+ .db 232 ; Arpeggio: -12.
+
+ .db 9 ; Volume: 2.
+
+ .db 137 ; Volume: 2.
+ .db 24 ; Arpeggio: 12.
+
+ .db 9 ; Volume: 2.
+
+ .db 137 ; Volume: 2.
+ .db 232 ; Arpeggio: -12.
+
+ .db 9 ; Volume: 2.
+
+ .db 4 ; End the instrument.
+KitsunesCurse_DisarkPointerRegionStart14:
+ .dw KitsunesCurse_FmInstrument0Loop ; Loop to silence.
+KitsunesCurse_DisarkPointerRegionEnd14:
+
+KitsunesCurse_DisarkByteRegionEnd7:
+KitsunesCurse_Subsong0DisarkByteRegionStart0:
+; Song Kitsune's Curse, Subsong 0 - Silence - in Lightweight format (V1).
+; Generated by Arkos Tracker 2.
+
+KitsunesCurse_Subsong0:
+ .db 6 ; Initial speed.
+
+; The Linker.
+; Pattern 0
+ .db 5 ; State byte.
+ .db 21 ; New height.
+KitsunesCurse_Subsong0DisarkPointerRegionStart1:
+ .dw KitsunesCurse_Subsong0_Track0, KitsunesCurse_Subsong0_Track0, KitsunesCurse_Subsong0_Track1
+KitsunesCurse_Subsong0DisarkPointerRegionEnd1:
+; The tracks.
+
+; Pattern 1
+KitsunesCurse_Subsong0loop:
+ .db 1 ; State byte.
+KitsunesCurse_Subsong0DisarkPointerRegionStart2:
+ .dw KitsunesCurse_Subsong0_Track0, KitsunesCurse_Subsong0_Track0, KitsunesCurse_Subsong0_Track0
+KitsunesCurse_Subsong0DisarkPointerRegionEnd2:
+; The tracks.
+
+ .db 0 ; End of the subsong.
+KitsunesCurse_Subsong0DisarkPointerRegionStart3:
+ .dw KitsunesCurse_Subsong0loop
+KitsunesCurse_Subsong0DisarkPointerRegionEnd3:
+
+; The Tracks.
+KitsunesCurse_Subsong0_Track0:
+ .db 61, 21 ; Long wait: 22.
+
+
+KitsunesCurse_Subsong0_Track1:
+ .db 152 ; Note: 48.
+ .db 0 ; New instrument: 0.
+ .db 61, 20 ; Long wait: 21.
+
+
+KitsunesCurse_Subsong0DisarkByteRegionEnd0:
+KitsunesCurse_Subsong1DisarkByteRegionStart0:
+; Song Kitsune's Curse, Subsong 1 - Split the stone - in Lightweight format (V1).
+; Generated by Arkos Tracker 2.
+
+KitsunesCurse_Subsong1:
+ .db 6 ; Initial speed.
+
+; The Linker.
+; Pattern 0
+KitsunesCurse_Subsong1loop:
+ .db 5 ; State byte.
+ .db 47 ; New height.
+KitsunesCurse_Subsong1DisarkPointerRegionStart1:
+ .dw KitsunesCurse_Subsong1_Track1, KitsunesCurse_Subsong1_Track0, KitsunesCurse_Subsong1_Track2
+KitsunesCurse_Subsong1DisarkPointerRegionEnd1:
+; The tracks.
+
+ .db 0 ; End of the subsong.
+KitsunesCurse_Subsong1DisarkPointerRegionStart2:
+ .dw KitsunesCurse_Subsong1loop
+KitsunesCurse_Subsong1DisarkPointerRegionEnd2:
+
+; The Tracks.
+KitsunesCurse_Subsong1_Track0:
+ .db 128 ; Note: 24.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 0 ; Note: 24.
+ .db 61, 4 ; Long wait: 5.
+
+ .db 0 ; Note: 24.
+ .db 62 ; Short wait: 1.
+
+ .db 0 ; Note: 24.
+ .db 61, 4 ; Long wait: 5.
+
+ .db 0 ; Note: 24.
+ .db 62 ; Short wait: 1.
+
+ .db 0 ; Note: 24.
+ .db 61, 4 ; Long wait: 5.
+
+ .db 3 ; Note: 27.
+ .db 62 ; Short wait: 1.
+
+ .db 3 ; Note: 27.
+ .db 190 ; Short wait: 3.
+
+ .db 3 ; Note: 27.
+ .db 62 ; Short wait: 1.
+
+ .db 0 ; Note: 24.
+ .db 61, 14 ; Long wait: 15.
+
+
+KitsunesCurse_Subsong1_Track1:
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 190 ; Short wait: 3.
+
+ .db 172 ; Note: 68.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 24 ; Note: 48.
+ .db 61, 4 ; Long wait: 5.
+
+ .db 172 ; Note: 68.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 190 ; Short wait: 3.
+
+ .db 172 ; Note: 68.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 27 ; Note: 51.
+ .db 190 ; Short wait: 3.
+
+ .db 27 ; Note: 51.
+ .db 62 ; Short wait: 1.
+
+ .db 27 ; Note: 51.
+ .db 62 ; Short wait: 1.
+
+ .db 164 ; Note: 60.
+ .db 8 ; New instrument: 4.
+ .db 61, 14 ; Long wait: 15.
+
+
+KitsunesCurse_Subsong1_Track2:
+ .db 152 ; Note: 48.
+ .db 2 ; New instrument: 1.
+ .db 62 ; Short wait: 1.
+
+ .db 39 ; Note: 63.
+ .db 62 ; Short wait: 1.
+
+ .db 24 ; Note: 48.
+ .db 62 ; Short wait: 1.
+
+ .db 41 ; Note: 65.
+ .db 62 ; Short wait: 1.
+
+ .db 24 ; Note: 48.
+ .db 62 ; Short wait: 1.
+
+ .db 43 ; Note: 67.
+ .db 62 ; Short wait: 1.
+
+ .db 24 ; Note: 48.
+ .db 62 ; Short wait: 1.
+
+ .db 44 ; Note: 68.
+ .db 62 ; Short wait: 1.
+
+ .db 24 ; Note: 48.
+ .db 62 ; Short wait: 1.
+
+ .db 43 ; Note: 67.
+ .db 62 ; Short wait: 1.
+
+ .db 24 ; Note: 48.
+ .db 62 ; Short wait: 1.
+
+ .db 39 ; Note: 63.
+ .db 62 ; Short wait: 1.
+
+ .db 38 ; Note: 62.
+ .db 62 ; Short wait: 1.
+
+ .db 24 ; Note: 48.
+ .db 62 ; Short wait: 1.
+
+ .db 38 ; Note: 62.
+ .db 62 ; Short wait: 1.
+
+ .db 39 ; Note: 63.
+ .db 62 ; Short wait: 1.
+
+ .db 36 ; Note: 60.
+ .db 61, 14 ; Long wait: 15.
+
+
+KitsunesCurse_Subsong1DisarkByteRegionEnd0:
+KitsunesCurse_Subsong2DisarkByteRegionStart0:
+; Song Kitsune's Curse, Subsong 2 - The Curse - in Lightweight format (V1).
+; Generated by Arkos Tracker 2.
+
+KitsunesCurse_Subsong2:
+ .db 6 ; Initial speed.
+
+; The Linker.
+; Pattern 0
+KitsunesCurse_Subsong2loop:
+ .db 5 ; State byte.
+ .db 31 ; New height.
+KitsunesCurse_Subsong2DisarkPointerRegionStart1:
+ .dw KitsunesCurse_Subsong2_Track0, KitsunesCurse_Subsong2_Track1, KitsunesCurse_Subsong2_Track2
+KitsunesCurse_Subsong2DisarkPointerRegionEnd1:
+; The tracks.
+
+; Pattern 1
+ .db 1 ; State byte.
+KitsunesCurse_Subsong2DisarkPointerRegionStart2:
+ .dw KitsunesCurse_Subsong2_Track8, KitsunesCurse_Subsong2_Track5, KitsunesCurse_Subsong2_Track2
+KitsunesCurse_Subsong2DisarkPointerRegionEnd2:
+; The tracks.
+
+; Pattern 2
+ .db 5 ; State byte.
+ .db 63 ; New height.
+KitsunesCurse_Subsong2DisarkPointerRegionStart3:
+ .dw KitsunesCurse_Subsong2_Track3, KitsunesCurse_Subsong2_Track4, KitsunesCurse_Subsong2_Track2
+KitsunesCurse_Subsong2DisarkPointerRegionEnd3:
+; The tracks.
+
+; Pattern 3
+ .db 1 ; State byte.
+KitsunesCurse_Subsong2DisarkPointerRegionStart4:
+ .dw KitsunesCurse_Subsong2_Track3, KitsunesCurse_Subsong2_Track4, KitsunesCurse_Subsong2_Track2
+KitsunesCurse_Subsong2DisarkPointerRegionEnd4:
+; The tracks.
+
+; Pattern 4
+ .db 1 ; State byte.
+KitsunesCurse_Subsong2DisarkPointerRegionStart5:
+ .dw KitsunesCurse_Subsong2_Track6, KitsunesCurse_Subsong2_Track7, KitsunesCurse_Subsong2_Track2
+KitsunesCurse_Subsong2DisarkPointerRegionEnd5:
+; The tracks.
+
+; Pattern 5
+ .db 1 ; State byte.
+KitsunesCurse_Subsong2DisarkPointerRegionStart6:
+ .dw KitsunesCurse_Subsong2_Track6, KitsunesCurse_Subsong2_Track4, KitsunesCurse_Subsong2_Track2
+KitsunesCurse_Subsong2DisarkPointerRegionEnd6:
+; The tracks.
+
+; Pattern 6
+ .db 1 ; State byte.
+KitsunesCurse_Subsong2DisarkPointerRegionStart7:
+ .dw KitsunesCurse_Subsong2_Track9, KitsunesCurse_Subsong2_Track7, KitsunesCurse_Subsong2_Track2
+KitsunesCurse_Subsong2DisarkPointerRegionEnd7:
+; The tracks.
+
+; Pattern 7
+ .db 1 ; State byte.
+KitsunesCurse_Subsong2DisarkPointerRegionStart8:
+ .dw KitsunesCurse_Subsong2_Track10, KitsunesCurse_Subsong2_Track11, KitsunesCurse_Subsong2_Track2
+KitsunesCurse_Subsong2DisarkPointerRegionEnd8:
+; The tracks.
+
+ .db 0 ; End of the subsong.
+KitsunesCurse_Subsong2DisarkPointerRegionStart9:
+ .dw KitsunesCurse_Subsong2loop
+KitsunesCurse_Subsong2DisarkPointerRegionEnd9:
+
+; The Tracks.
+KitsunesCurse_Subsong2_Track0:
+ .db 161 ; Note: 57.
+ .db 2 ; New instrument: 1.
+ .db 34 ; Note: 58.
+ .db 33 ; Note: 57.
+ .db 31 ; Note: 55.
+ .db 33 ; Note: 57.
+ .db 34 ; Note: 58.
+ .db 33 ; Note: 57.
+ .db 31 ; Note: 55.
+ .db 33 ; Note: 57.
+ .db 34 ; Note: 58.
+ .db 33 ; Note: 57.
+ .db 31 ; Note: 55.
+ .db 33 ; Note: 57.
+ .db 34 ; Note: 58.
+ .db 33 ; Note: 57.
+ .db 31 ; Note: 55.
+ .db 28 ; Note: 52.
+ .db 29 ; Note: 53.
+ .db 28 ; Note: 52.
+ .db 26 ; Note: 50.
+ .db 28 ; Note: 52.
+ .db 29 ; Note: 53.
+ .db 28 ; Note: 52.
+ .db 26 ; Note: 50.
+ .db 28 ; Note: 52.
+ .db 29 ; Note: 53.
+ .db 28 ; Note: 52.
+ .db 26 ; Note: 50.
+ .db 28 ; Note: 52.
+ .db 29 ; Note: 53.
+ .db 28 ; Note: 52.
+ .db 26 ; Note: 50.
+ .db 61, 31 ; Long wait: 32.
+
+
+KitsunesCurse_Subsong2_Track1:
+ .db 142 ; Note: 38.
+ .db 8 ; New instrument: 4.
+ .db 130 ; Note: 26.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 2 ; Note: 26.
+ .db 154 ; Note: 50.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 130 ; Note: 26.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 8 ; New instrument: 4.
+ .db 130 ; Note: 26.
+ .db 4 ; New instrument: 2.
+ .db 126 ; Short wait: 2.
+
+ .db 154 ; Note: 50.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 26 ; Note: 50.
+ .db 140 ; Note: 36.
+ .db 8 ; New instrument: 4.
+ .db 128 ; Note: 24.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 0 ; Note: 24.
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 128 ; Note: 24.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 8 ; New instrument: 4.
+ .db 128 ; Note: 24.
+ .db 4 ; New instrument: 2.
+ .db 126 ; Short wait: 2.
+
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 129 ; Note: 25.
+ .db 4 ; New instrument: 2.
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 61, 31 ; Long wait: 32.
+
+
+KitsunesCurse_Subsong2_Track2:
+ .db 61, 63 ; Long wait: 64.
+
+
+KitsunesCurse_Subsong2_Track3:
+ .db 159 ; Note: 55.
+ .db 12 ; New instrument: 6.
+ .db 62 ; Short wait: 1.
+
+ .db 33 ; Note: 57.
+ .db 62 ; Short wait: 1.
+
+ .db 29 ; Note: 53.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 190 ; Short wait: 3.
+
+ .db 33 ; Note: 57.
+ .db 190 ; Short wait: 3.
+
+ .db 29 ; Note: 53.
+ .db 62 ; Short wait: 1.
+
+ .db 28 ; Note: 52.
+ .db 62 ; Short wait: 1.
+
+ .db 29 ; Note: 53.
+ .db 62 ; Short wait: 1.
+
+ .db 31 ; Note: 55.
+ .db 62 ; Short wait: 1.
+
+ .db 28 ; Note: 52.
+ .db 190 ; Short wait: 3.
+
+ .db 24 ; Note: 48.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 28 ; Note: 52.
+ .db 62 ; Short wait: 1.
+
+ .db 29 ; Note: 53.
+ .db 62 ; Short wait: 1.
+
+ .db 28 ; Note: 52.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 33 ; Note: 57.
+ .db 61, 4 ; Long wait: 5.
+
+ .db 29 ; Note: 53.
+ .db 62 ; Short wait: 1.
+
+ .db 28 ; Note: 52.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 33 ; Note: 57.
+ .db 61, 4 ; Long wait: 5.
+
+ .db 31 ; Note: 55.
+ .db 62 ; Short wait: 1.
+
+ .db 33 ; Note: 57.
+ .db 62 ; Short wait: 1.
+
+ .db 29 ; Note: 53.
+ .db 62 ; Short wait: 1.
+
+ .db 28 ; Note: 52.
+ .db 62 ; Short wait: 1.
+
+
+KitsunesCurse_Subsong2_Track4:
+ .db 142 ; Note: 38.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 4 ; New instrument: 2.
+ .db 14 ; Note: 38.
+ .db 154 ; Note: 50.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 154 ; Note: 50.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 4 ; New instrument: 2.
+ .db 12 ; Note: 36.
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 4 ; New instrument: 2.
+ .db 17 ; Note: 41.
+ .db 157 ; Note: 53.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 157 ; Note: 53.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 4 ; New instrument: 2.
+ .db 19 ; Note: 43.
+ .db 159 ; Note: 55.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 4 ; New instrument: 2.
+ .db 159 ; Note: 55.
+ .db 6 ; New instrument: 3.
+ .db 31 ; Note: 55.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 4 ; New instrument: 2.
+ .db 159 ; Note: 55.
+ .db 6 ; New instrument: 3.
+
+KitsunesCurse_Subsong2_Track5:
+ .db 142 ; Note: 38.
+ .db 8 ; New instrument: 4.
+ .db 130 ; Note: 26.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 2 ; Note: 26.
+ .db 154 ; Note: 50.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 130 ; Note: 26.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 8 ; New instrument: 4.
+ .db 130 ; Note: 26.
+ .db 4 ; New instrument: 2.
+ .db 126 ; Short wait: 2.
+
+ .db 154 ; Note: 50.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 26 ; Note: 50.
+ .db 140 ; Note: 36.
+ .db 8 ; New instrument: 4.
+ .db 128 ; Note: 24.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 0 ; Note: 24.
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 128 ; Note: 24.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 8 ; New instrument: 4.
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 24 ; Note: 48.
+ .db 62 ; Short wait: 1.
+
+ .db 24 ; Note: 48.
+ .db 62 ; Short wait: 1.
+
+ .db 25 ; Note: 49.
+ .db 24 ; Note: 48.
+ .db 61, 31 ; Long wait: 32.
+
+
+KitsunesCurse_Subsong2_Track6:
+ .db 161 ; Note: 57.
+ .db 10 ; New instrument: 5.
+ .db 190 ; Short wait: 3.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 0 ; New instrument: 0.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 10 ; New instrument: 5.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 0 ; New instrument: 0.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 10 ; New instrument: 5.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 24 ; Note: 48.
+ .db 62 ; Short wait: 1.
+
+ .db 29 ; Note: 53.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 0 ; New instrument: 0.
+ .db 190 ; Short wait: 3.
+
+ .db 161 ; Note: 57.
+ .db 10 ; New instrument: 5.
+ .db 190 ; Short wait: 3.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 0 ; New instrument: 0.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 10 ; New instrument: 5.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 0 ; New instrument: 0.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 10 ; New instrument: 5.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 24 ; Note: 48.
+ .db 62 ; Short wait: 1.
+
+ .db 29 ; Note: 53.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 0 ; New instrument: 0.
+ .db 190 ; Short wait: 3.
+
+
+KitsunesCurse_Subsong2_Track7:
+ .db 142 ; Note: 38.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 4 ; New instrument: 2.
+ .db 14 ; Note: 38.
+ .db 154 ; Note: 50.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 154 ; Note: 50.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 4 ; New instrument: 2.
+ .db 12 ; Note: 36.
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 4 ; New instrument: 2.
+ .db 17 ; Note: 41.
+ .db 157 ; Note: 53.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 157 ; Note: 53.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 4 ; New instrument: 2.
+ .db 19 ; Note: 43.
+ .db 159 ; Note: 55.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 159 ; Note: 55.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 4 ; New instrument: 2.
+ .db 159 ; Note: 55.
+ .db 6 ; New instrument: 3.
+
+KitsunesCurse_Subsong2_Track8:
+ .db 161 ; Note: 57.
+ .db 2 ; New instrument: 1.
+ .db 34 ; Note: 58.
+ .db 33 ; Note: 57.
+ .db 31 ; Note: 55.
+ .db 33 ; Note: 57.
+ .db 34 ; Note: 58.
+ .db 33 ; Note: 57.
+ .db 31 ; Note: 55.
+ .db 33 ; Note: 57.
+ .db 34 ; Note: 58.
+ .db 33 ; Note: 57.
+ .db 31 ; Note: 55.
+ .db 33 ; Note: 57.
+ .db 34 ; Note: 58.
+ .db 33 ; Note: 57.
+ .db 31 ; Note: 55.
+ .db 28 ; Note: 52.
+ .db 29 ; Note: 53.
+ .db 28 ; Note: 52.
+ .db 26 ; Note: 50.
+ .db 28 ; Note: 52.
+ .db 29 ; Note: 53.
+ .db 28 ; Note: 52.
+ .db 26 ; Note: 50.
+ .db 28 ; Note: 52.
+ .db 190 ; Short wait: 3.
+
+ .db 28 ; Note: 52.
+ .db 62 ; Short wait: 1.
+
+ .db 28 ; Note: 52.
+ .db 26 ; Note: 50.
+ .db 61, 31 ; Long wait: 32.
+
+
+KitsunesCurse_Subsong2_Track9:
+ .db 161 ; Note: 57.
+ .db 10 ; New instrument: 5.
+ .db 190 ; Short wait: 3.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 0 ; New instrument: 0.
+ .db 62 ; Short wait: 1.
+
+ .db 161 ; Note: 57.
+ .db 2 ; New instrument: 1.
+ .db 34 ; Note: 58.
+ .db 33 ; Note: 57.
+ .db 31 ; Note: 55.
+ .db 33 ; Note: 57.
+ .db 190 ; Short wait: 3.
+
+ .db 152 ; Note: 48.
+ .db 10 ; New instrument: 5.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 24 ; Note: 48.
+ .db 62 ; Short wait: 1.
+
+ .db 29 ; Note: 53.
+ .db 190 ; Short wait: 3.
+
+ .db 154 ; Note: 50.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 28 ; Note: 52.
+ .db 62 ; Short wait: 1.
+
+ .db 161 ; Note: 57.
+ .db 10 ; New instrument: 5.
+ .db 190 ; Short wait: 3.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 0 ; New instrument: 0.
+ .db 62 ; Short wait: 1.
+
+ .db 161 ; Note: 57.
+ .db 2 ; New instrument: 1.
+ .db 34 ; Note: 58.
+ .db 33 ; Note: 57.
+ .db 31 ; Note: 55.
+ .db 33 ; Note: 57.
+ .db 190 ; Short wait: 3.
+
+ .db 152 ; Note: 48.
+ .db 10 ; New instrument: 5.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 24 ; Note: 48.
+ .db 62 ; Short wait: 1.
+
+ .db 31 ; Note: 55.
+ .db 62 ; Short wait: 1.
+
+ .db 157 ; Note: 53.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 28 ; Note: 52.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+
+KitsunesCurse_Subsong2_Track10:
+ .db 161 ; Note: 57.
+ .db 10 ; New instrument: 5.
+ .db 190 ; Short wait: 3.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 0 ; New instrument: 0.
+ .db 62 ; Short wait: 1.
+
+ .db 161 ; Note: 57.
+ .db 2 ; New instrument: 1.
+ .db 34 ; Note: 58.
+ .db 33 ; Note: 57.
+ .db 31 ; Note: 55.
+ .db 33 ; Note: 57.
+ .db 190 ; Short wait: 3.
+
+ .db 152 ; Note: 48.
+ .db 10 ; New instrument: 5.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 24 ; Note: 48.
+ .db 62 ; Short wait: 1.
+
+ .db 29 ; Note: 53.
+ .db 190 ; Short wait: 3.
+
+ .db 154 ; Note: 50.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 28 ; Note: 52.
+ .db 62 ; Short wait: 1.
+
+ .db 161 ; Note: 57.
+ .db 10 ; New instrument: 5.
+ .db 190 ; Short wait: 3.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 0 ; New instrument: 0.
+ .db 62 ; Short wait: 1.
+
+ .db 161 ; Note: 57.
+ .db 2 ; New instrument: 1.
+ .db 34 ; Note: 58.
+ .db 33 ; Note: 57.
+ .db 31 ; Note: 55.
+ .db 33 ; Note: 57.
+ .db 190 ; Short wait: 3.
+
+ .db 152 ; Note: 48.
+ .db 10 ; New instrument: 5.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 190 ; Short wait: 3.
+
+ .db 152 ; Note: 48.
+ .db 0 ; New instrument: 0.
+ .db 62 ; Short wait: 1.
+
+ .db 157 ; Note: 53.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 28 ; Note: 52.
+ .db 62 ; Short wait: 1.
+
+ .db 26 ; Note: 50.
+ .db 62 ; Short wait: 1.
+
+
+KitsunesCurse_Subsong2_Track11:
+ .db 142 ; Note: 38.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 4 ; New instrument: 2.
+ .db 14 ; Note: 38.
+ .db 154 ; Note: 50.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 154 ; Note: 50.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 142 ; Note: 38.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 4 ; New instrument: 2.
+ .db 12 ; Note: 36.
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 152 ; Note: 48.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 140 ; Note: 36.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 4 ; New instrument: 2.
+ .db 17 ; Note: 41.
+ .db 157 ; Note: 53.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 157 ; Note: 53.
+ .db 6 ; New instrument: 3.
+ .db 62 ; Short wait: 1.
+
+ .db 145 ; Note: 41.
+ .db 4 ; New instrument: 2.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 8 ; New instrument: 4.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 4 ; New instrument: 2.
+ .db 19 ; Note: 43.
+ .db 159 ; Note: 55.
+ .db 6 ; New instrument: 3.
+ .db 61, 4 ; Long wait: 5.
+
+ .db 147 ; Note: 43.
+ .db 4 ; New instrument: 2.
+ .db 159 ; Note: 55.
+ .db 6 ; New instrument: 3.
+ .db 31 ; Note: 55.
+ .db 62 ; Short wait: 1.
+
+ .db 147 ; Note: 43.
+ .db 4 ; New instrument: 2.
+ .db 159 ; Note: 55.
+ .db 6 ; New instrument: 3.
+
+KitsunesCurse_Subsong2DisarkByteRegionEnd0:
diff --git a/src/sound.h b/src/sound.h new file mode 100644 index 0000000..50a8822 --- /dev/null +++ b/src/sound.h @@ -0,0 +1,57 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _SOUND_H +#define _SOUND_H + +#include <stdint.h> + +enum effect_number +{ + EFX_NONE = 0, + EFX_HIT, + + EFX_SPIDER, + EFX_ONI, + EFX_TICK, + EFX_DOOR, + + EFX_MAGIC, + EFX_PICKUP, + EFX_DAMAGE, + EFX_DEATH, + EFX_SPLASH, +}; + +enum song_number +{ + SONG_SILENCE = 0, + SONG_SPLIT, + SONG_INGAME, +}; + +enum song_pak_number +{ + SONG_INTRO = 0, + SONG_MENU, + SONG_GAMEOVER, +}; + +extern uint8_t songs[]; +extern uint8_t effects[]; + +#endif // _SOUND_H diff --git a/src/sound.z80 b/src/sound.z80 new file mode 100644 index 0000000..42cac08 --- /dev/null +++ b/src/sound.z80 @@ -0,0 +1,8 @@ +.globl _songs +.globl _effects + +_songs:: +.include "songs.z80" + +_effects:: +.include "effects.z80" diff --git a/src/splib.c b/src/splib.c new file mode 100644 index 0000000..1869123 --- /dev/null +++ b/src/splib.c @@ -0,0 +1,2031 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include <stdlib.h> +#include <string.h> + +#include "splib.h" +#include "font.h" + +//#define SPRITE_DEBUG +//#define FENCE_DEBUG + +struct st_tile tiles[TMW * TMH]; +struct st_tile *dirty; +uint8_t *mini_buffer; + +// faster +struct st_tile *tile_p; + +#ifdef FENCE_DEBUG +// (sprite th * 2 + 2) * msx sprites +#define MAX_MINI_BUFFER (3 * 2 + 2) * 10 +uint8_t mini_buffer_cnt; +#endif + +void pad_numbers(uint8_t *s, uint8_t limit, uint16_t number) +{ + s += limit; + *s = 0; + + do + { + limit--; + *--s = (number % 10) + '0'; + number /= 10; + } while (limit); +} + +#pragma save +#pragma disable_warning 85 +void set_hw_border(uint8_t c) __z88dk_fastcall +{ + // *INDENT-OFF* + __asm; + ld bc, #0x7f10 + out (c), c + ld c, l + out (c), c + __endasm; + // *INDENT-ON* +} +#pragma restore + +#pragma save +#pragma disable_warning 85 +void set_hw_ink(uint8_t ink, uint8_t c) +{ + // *INDENT-OFF* + __asm; + ld hl, #2 + add hl, sp + ld a, (hl) + inc hl + ld e, (hl) + ld bc, #0x7f00 + out (c), a + ld a, #0x40 + or e + out (c), a + __endasm; + // *INDENT-ON* +} +#pragma restore + +void wait_vsync() +{ + // *INDENT-OFF* + __asm; + ld b, #0xf5 +keep_waiting: + in a, (c) + rra + jr nc, keep_waiting + __endasm; + // *INDENT-ON* +} + +void clear_screen() +{ + memset((uint8_t*)0xc000, 0, 16383); +} + +// this is quite slow, use it only where speed is not an issue +uint16_t screen_addr(uint16_t x, uint16_t y) +{ + // up to 160 x 200 + return (0xc000 + x + ((y / 8) * 80) + ((y % 8) * 2048)); +} + +void init_tiles() +{ + uint8_t i, j; + + memset(tiles, 0, sizeof(struct st_tile) * TMW * TMH); + + for (j = 0; j < TMH; ++j) + for (i = 0; i < TMW; ++i) + tiles[i + j * TMW].saddr = screen_addr((uint16_t)i * TW / 2, 32 + (uint16_t)j * TH); + + dirty = NULL; + mini_buffer = (uint8_t *)BUFF_ADDR; + +#ifdef FENCE_DEBUG + mini_buffer_cnt = 0; +#endif +} + +void update_screen() +{ + // tiles are expected to be 8x8 pixels + // *INDENT-OFF* + __asm; + call _wait_int6 + + ld hl, (_dirty) + +update_loop: + ld a, h + or l + jr z, update_done + + ; baddr in de; and reset it + xor a + ld e, (hl) + ld (hl), a + inc hl + ld d, (hl) + ld (hl), a + inc hl + + ; clean dirty flag + res 7, d + ld a, d + or e + jr nz, linked_buffer + + ; not linked, use the original tile + ; hl has *t + ld e, (hl) + inc hl + ld d, (hl) + dec hl + +linked_buffer: + ; skip *t + inc hl + inc hl + + ; saddr in a, c + ld c, (hl) + inc hl + ld a, (hl) + inc hl + + ; pointing to next + push hl + + ; baddr in hl + ex de, hl + ; saddr to de + ld e, c + ld d, a + + ld a, #8 +put_tile0: + ldi + ldi + ldi + ldi + + dec a + jp z, put_tile2 + + ; inc destination + ex de, hl + ld bc, #0x07fc + add hl, bc + jp nc, put_tile1 + ld bc, #0xc048 + add hl, bc +put_tile1: + ex de, hl + jr put_tile0 + +put_tile2: + ; next dirty tile + pop hl + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + + jp update_loop + +update_done: + + ; a is 0 + ld (_dirty), a + ld (_dirty + #1), a + +#ifdef FENCE_DEBUG + ld (_mini_buffer_cnt), a +#endif + + ; reset mini buffers (begin buffer at 0x0100) + ld (_mini_buffer), a + inc a + ld (_mini_buffer + #1), a + __endasm; + // *INDENT-ON* +} + +void validate_screen() +{ + for (; dirty; dirty = dirty->n) + dirty->baddr = 0; + + // dirty is NULL + mini_buffer = (uint8_t *)BUFF_ADDR; +} + +void invalidate_screen() +{ + uint16_t i; + + dirty = NULL; + mini_buffer = (uint8_t *)BUFF_ADDR; + + for (i = TMW * TMH; i; i--) + { + tiles[i - 1].baddr &= ADDR_BITS; + invalidate_tile(&tiles[i - 1]); + } +} + +void invalidate_tile(struct st_tile *st) __z88dk_fastcall +{ + /* + if (st->baddr & DIRTY_BIT) + return; + + st->baddr |= DIRTY_BIT; + st->n = dirty; + dirty = st; + */ + // *INDENT-OFF* + __asm; + ld e, l + ld d, h + + ; test for dirty bit + inc hl + ld a, (hl) + bit #7, a + ret nz + + ; set dirty bit + set #7, a + ld (hl), a + + ; set *n to dirty + ld a, #5 + add a, l + ld l, a + adc a, h + sub l + ld h, a + + ld a, (#_dirty) + ld (hl), a + inc hl + ld a, (#_dirty + #1) + ld (hl), a + + ; set dirty to *st + ld (_dirty), de + __endasm; + // *INDENT-ON* +} + +struct st_tile *get_tile_xy(uint8_t x, uint8_t y) +{ + // x and y in tilemap coordinates + +#ifdef FENCE_DEBUG + if (x > TMW || y > TMH) + set_hw_border(0x4b); +#endif + + return &tiles[x + (uint16_t)y * TMW]; +} + +#pragma save +#pragma disable_warning 59 +uint8_t is_invalid_tile(struct st_tile *st) __z88dk_fastcall +{ + // *INDENT-OFF* + __asm; + inc hl + ld a, (hl) + and #128 + ld l, a + __endasm; + // *INDENT-ON* +} +#pragma restore + +#pragma save +#pragma disable_warning 59 +uint8_t is_invalid_tile2(struct st_tile *st) __z88dk_fastcall +{ + // *INDENT-OFF* + __asm; + inc hl + ld a, (hl) + and #128 + jr nz, is_invalid + ld bc, #(TMW * 8) + add hl, bc + ld a, (hl) + and #128 +is_invalid: + ld l, a + __endasm; + // *INDENT-ON* +} +#pragma restore + +void erase_tile(struct st_tile *st) __z88dk_fastcall +{ + // *INDENT-OFF* + __asm; + ; if it has a linked buffer, it was erased already + ; testing MSB only + inc hl + ld a, (hl) + or a + ret nz + + dec hl + + ; and if there is no linked buffer, it is not invalidated + + ; set dirty bit (if not set already) + jp _invalidate_tile + __endasm; + // *INDENT-ON* +} + +void link_buffer(struct st_tile *st) __z88dk_fastcall +{ + // *INDENT-OFF* + __asm; + ; check is not linked already + ; testing MSB only + inc hl + ld a, (hl) + and #127 + ret nz + + dec hl + +#ifdef FENCE_DEBUG + ld a, (_mini_buffer_cnt) + inc a + ld (_mini_buffer_cnt), a + + cp #MAX_MINI_BUFFER + jp c, mini_buffers_ok + + ld hl, #0x58 + jp _set_hw_border + +mini_buffers_ok: +#endif + + ; save *st + push hl + + ; allocate a new buffer + ex de, hl + ld hl, (_mini_buffer) + ld c, l + ld b, h + ex de, hl + + ; new buffer to baddr, preserve dirty bit + ld (hl), c + inc hl + ld a, (hl) + and #128 + or b + ld (hl), a + dec hl + + ; move mini buffer pointer + ex de, hl + ld hl, #(TW * TH / 2) + add hl, bc + ld (_mini_buffer), hl + ex de, hl + + ; baddr to de + ld e, c + ld d, b + + ; *t to hl + inc hl + inc hl + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + +#ifdef SPRITE_DEBUG + ld hl, #_bgtiles + 14 * 32 +#endif + + ldi + ldi + ldi + ldi + + ldi + ldi + ldi + ldi + + ldi + ldi + ldi + ldi + + ldi + ldi + ldi + ldi + + ldi + ldi + ldi + ldi + + ldi + ldi + ldi + ldi + + ldi + ldi + ldi + ldi + + ldi + ldi + ldi + ldi + + ; *st + pop hl + jp _invalidate_tile + __endasm; + // *INDENT-ON* +} + +void put_tile(const uint8_t *t, struct st_tile *st) +{ + st->t = t; + invalidate_tile(st); +} + +#pragma save +#pragma disable_warning 85 +void erase_sprite(uint8_t x, uint8_t y, uint8_t th) +{ + // for 8 pixels wide sprites, th in tiles + // *INDENT-OFF* + __asm; + ld hl, #2 + add hl, sp + ld c, (hl) ; x + inc hl + ld e, (hl) ; y + inc hl + ld b, (hl) ; th + + ld a, e + + ; out of bounds check + cp #(TMH * TH - 8) + jr nc, no_inc_th + + and #0x07 + jr z, no_inc_th + inc b + +no_inc_th: + ld l, c ; x + srl l + srl l + srl l + + ld a, e ; y + srl a + srl a + srl a + add a, b ; add th + ld h, a + + push bc + push hl + call _get_tile_xy + pop af + pop bc + ; hl has the tile addr + + .db 0xdd + ld l, b + + ld a, c + and #0x07 + jr z, erase_tile_loop_no_extra_x + +erase_tile_loop: + xor a + ld de, #(TMW * 8) + sbc hl, de + ; dec tile ptr 1 row as th decrements + + push hl + call _erase_tile + pop hl + + push hl + ld de, #8 + add hl, de + ; tile ptr + 1 + call _erase_tile + pop hl + + .db 0xdd + dec l + jr nz, erase_tile_loop + ret + +erase_tile_loop_no_extra_x: + xor a + ld de, #(TMW * 8) + sbc hl, de + ; dec tile ptr 1 row as th decrements + + push hl + call _erase_tile + pop hl + + .db 0xdd + dec l + jr nz, erase_tile_loop_no_extra_x + + __endasm; + // *INDENT-ON* +} +#pragma restore + +const uint8_t inv_table[] = { + 0, 2, 1, 3, 8, 10, 9, 11, 4, 6, 5, 7, 12, 14, 13, 15, 32, 34, 33, 35, 40, + 42, 41, 43, 36, 38, 37, 39, 44, 46, 45, 47, 16, 18, 17, 19, 24, 26, 25, 27, + 20, 22, 21, 23, 28, 30, 29, 31, 48, 50, 49, 51, 56, 58, 57, 59, 52, 54, 53, + 55, 60, 62, 61, 63, 128, 130, 129, 131, 136, 138, 137, 139, 132, 134, 133, + 135, 140, 142, 141, 143, 160, 162, 161, 163, 168, 170, 169, 171, 164, 166, + 165, 167, 172, 174, 173, 175, 144, 146, 145, 147, 152, 154, 153, 155, 148, + 150, 149, 151, 156, 158, 157, 159, 176, 178, 177, 179, 184, 186, 185, 187, + 180, 182, 181, 183, 188, 190, 189, 191, 64, 66, 65, 67, 72, 74, 73, 75, 68, + 70, 69, 71, 76, 78, 77, 79, 96, 98, 97, 99, 104, 106, 105, 107, 100, 102, + 101, 103, 108, 110, 109, 111, 80, 82, 81, 83, 88, 90, 89, 91, 84, 86, 85, + 87, 92, 94, 93, 95, 112, 114, 113, 115, 120, 122, 121, 123, 116, 118, 117, + 119, 124, 126, 125, 127, 192, 194, 193, 195, 200, 202, 201, 203, 196, 198, + 197, 199, 204, 206, 205, 207, 224, 226, 225, 227, 232, 234, 233, 235, 228, + 230, 229, 231, 236, 238, 237, 239, 208, 210, 209, 211, 216, 218, 217, 219, + 212, 214, 213, 215, 220, 222, 221, 223, 240, 242, 241, 243, 248, 250, 249, + 251, 244, 246, 245, 247, 252, 254, 253, 255 +}; + +#pragma save +#pragma disable_warning 85 +void put_sprite_rc_nm(uint8_t *d, uint8_t dox, uint8_t doy, uint8_t w, uint8_t h, const uint8_t *s, uint8_t sox, uint8_t soy) +{ + // x offset must be even + + /* + uint8_t i, j; + + d += dox + doy * TW / 2; + s += sox + soy * TW / 2; + + for (j = 0; j < h; j++) + { + for (i = 0; i < w; i++) + *d++ = *s++; + + s += (TW / 2) - w; + d += (TW / 2) - w; + } + + */ + // *INDENT-OFF* + __asm; + + ld hl, #2 + add hl, sp + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ; de has *d + + ld b, #0 + ld c, (hl) + ; bc has dox + inc hl + ld a, (hl) + sla a + sla a + ex de, hl + ; add dox + add hl, bc + ld c, a + ; bc has doy * TW / 2 + add hl, bc + ex de, hl + + ; d* is ready + push de + + inc hl + inc hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ; de has *s + + ; b is 0 + ld c, (hl) + ; bc has sox + inc hl + ld a, (hl) + sla a + sla a + ; a has soy * TW / 2 + ex de, hl + ; add sox + add hl, bc + ld c, a + ; add soy * TW / 2 + add hl, bc + ex de, hl + + ld hl, #(6 + 2) + add hl, sp + ld c, (hl) + inc hl + ; c is w + + ; the reminder + ld a, #4 + sub c + ld (no_inv_sub_w_nm + 1), a + + ; and build jump addr + sla a + ld (no_inv_inner_w_nm + 1), a + + + ld a, (hl) + ; h + + ; hl has *s + ex de, hl + + ; *d + pop de + + ld b, #0 +no_inv_height_nm: + +no_inv_inner_w_nm: + jr no_inv_inner_w_nm + + ldi + ldi + ldi + ldi + + ; b is 0 + + ; inc *d +no_inv_sub_w_nm: + ld c, #0 + + ex de, hl + add hl, bc + ex de, hl + + ; inc *s + add hl, bc + + dec a + jr nz, no_inv_height_nm + + __endasm; + // *INDENT-ON* +} +#pragma restore + +#pragma save +#pragma disable_warning 85 +void put_sprite_rc(uint8_t *d, uint8_t dox, uint8_t doy, uint8_t w, uint8_t h, const uint8_t *s, uint8_t sox, uint8_t soy, uint8_t inv) +{ + /* + uint8_t i, j; + + d += dox + doy * TW / 2; + + if (inv) + s += TW - 2 * (1 + sox) + soy * TW; + else + s += 2 * sox + soy * TW; + + if (inv) + { + for (j = 0; j < h; j++) + { + for (i = 0; i < w; i++) + { + *d &= inv_table[*s++]; + *d++ |= inv_table[*s]; + s -= 3; + } + s += (TW / 2 + w) * 2; + d += TW / 2 - w; + } + } + else + { + for (j = 0; j < h; j++) + { + for (i = 0; i < w; i++) + { + *d &= *s++; + *d++ |= *s++; + } + s += (TW / 2 - w) * 2; + d += TW / 2 - w; + } + } + */ + // *INDENT-OFF* + __asm; + + ld hl, #2 + add hl, sp + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ; de has *d + + ld b, #0 + ld c, (hl) + ; bc has dox + inc hl + ld a, (hl) + sla a + sla a + ex de, hl + add hl, bc + ld c, a + ; bc has doy * TW / 2 + add hl, bc + ex de, hl + + ; d* is ready + push de + + inc hl + inc hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ; de has *s + + ; b is 0 + inc hl + ld c, (hl) + sla c + sla c + sla c + ; bc has soy + ex de, hl + add hl, bc + ex de, hl + + ; common *s part is ready + inc hl + ld a, (hl) + or a + jr z, setup_no_inv + + ; blit inv + + ld hl, #(10 + 2) + add hl, sp + + ; b is 0 + ld c, (hl) + inc c + sla c + xor a + sub a, c + ld c, a + ld a, #0 + sbc b + ld b, a + ; bc has - 2 - 2 x sox + ex de, hl + add hl, bc + ld bc, #8 + ; add TW + add hl, bc + ex de, hl + + ; de has *s ready for inv + + ld hl, #(6 + 2) + add hl, sp + + ld a, (hl) + ; w + + ; set this in the code + ld (inv_inner_w + 1), a + + ld b, a + ld a, #4 + sub b + ld (inv_sub_w + 1), a + + ld a, #4 + add b + sla a + ld (inv_add_w + 1), a + + inc hl + + ld a, (hl) + + ; *d + pop hl + ex de, hl + + push ix + .db 0xdd + ld h, a + ; h + +inv_height: + + .db 0xdd +inv_inner_w: + ld l, #0 + +inv_width: + + ; add 8-bit number to a 16-bit reg + ; + ; add a, l + ; ld l, a + ; adc a, h + ; sub l + ; ld h, a + + ; first flip bg + ld a, (de) + + ld bc, #_inv_table + add a, c + ld c, a + adc a, b + sub c + ld b, a + + ld a, (bc) + + ; and mask; or sprite + and (hl) + inc hl + or (hl) + + ; flip again + + ld bc, #_inv_table + add a, c + ld c, a + adc a, b + sub c + ld b, a + + ld a, (bc) + + ld (de), a + inc de + + dec hl + dec hl + dec hl + + .db 0xdd + dec l + jr nz, inv_width + + ; inc *s +inv_add_w: + ld a, #0 + add a, l + ld l, a + adc a, h + sub l + ld h, a + + ; inc *d +inv_sub_w: + ld a, #0 + add a, e + ld e, a + adc a, d + sub e + ld d, a + + .db 0xdd + dec h + jr nz, inv_height + + pop ix + + ret + +setup_no_inv: + + ld hl, #(10 + 2) + add hl, sp + + ; b is 0 + ld c, (hl) + sla c + ex de, hl + add hl, bc + ; add 2 x sox to *s + ex de, hl + + ld hl, #(6 + 2) + add hl, sp + ld a, (hl) + inc hl + ; w + ld (no_inv_inner_w + 1), a + + ld b, a + ld a, #4 + sub b + ld (no_inv_sub_w + 1), a + sla a + ld (no_inv_sub_w2 + 1), a + + ld c, (hl) + ; h + + ; hl has *s + ex de, hl + + ; *d + pop de + +no_inv_height: + +no_inv_inner_w: + ld b, #0 + +no_inv_width: + + ; and mask; or sprite + ld a, (de) + and (hl) + inc hl + or (hl) + inc hl + ld (de), a + inc de + + djnz no_inv_width + + ; inc *d +no_inv_sub_w: + ld a, #0 + add a, e + ld e, a + adc a, d + sub e + ld d, a + + ; inc *s (x2) +no_inv_sub_w2: + ld a, #0 + add a, l + ld l, a + adc a, h + sub l + ld h, a + + dec c + jr nz, no_inv_height + + __endasm; + // *INDENT-ON* +} +#pragma restore + +void put_sprite4(const uint8_t *s, uint8_t x, uint8_t y, uint8_t th, uint8_t inv) +{ + /* + uint8_t xoffs, yoffs; + + xoffs = (x % TW) / 2; + yoffs = y % TH; + + tile_p = get_tile_xy(x >> 3, (y >> 3)); + + // top + link_buffer(tile_p); + put_sprite_rc((uint8_t *)(tile_p->baddr & ADDR_BITS), xoffs, yoffs, TW / 2 - xoffs, TH - yoffs, s, 0, 0, inv); + if (xoffs) + { + tile_p++; + link_buffer(tile_p); + put_sprite_rc((uint8_t *)(tile_p->baddr & ADDR_BITS), 0, yoffs, xoffs, TH - yoffs, s, TW / 2 - xoffs, 0, inv); + } + + if (th > 1) + { + // center 1 + tile_p = get_tile_xy(x >> 3, (y >> 3) + 1); + + link_buffer(tile_p); + put_sprite_rc((uint8_t *)(tile_p->baddr & ADDR_BITS), xoffs, 0, TW / 2 - xoffs, TH, s, 0, TH - yoffs, inv); + if (xoffs) + { + tile_p++; + link_buffer(tile_p); + put_sprite_rc((uint8_t *)(tile_p->baddr & ADDR_BITS), 0, 0, xoffs, TH, s, TW / 2 - xoffs, TH - yoffs, inv); + } + + if (th > 2) + { + // center 2 + tile_p = get_tile_xy(x >> 3, (y >> 3) + 2); + + link_buffer(tile_p); + put_sprite_rc((uint8_t *)(tile_p->baddr & ADDR_BITS), xoffs, 0, TW / 2 - xoffs, TH, s, 0, 2 * TH - yoffs, inv); + if (xoffs) + { + tile_p++; + link_buffer(tile_p); + put_sprite_rc((uint8_t *)(tile_p->baddr & ADDR_BITS), 0, 0, xoffs, TH, s, TW / 2 - xoffs, 2 * TH - yoffs, inv); + } + + // reminder + if (yoffs) { + tile_p = get_tile_xy(x >> 3, (y >> 3) + 3); + + link_buffer(tile_p); + put_sprite_rc((uint8_t *)(tile_p->baddr & ADDR_BITS), xoffs, 0, TW / 2 - xoffs, yoffs, s, 0, 3 * TH - yoffs, inv); + if (xoffs) + { + tile_p++; + link_buffer(tile_p); + put_sprite_rc((uint8_t *)(tile_p->baddr & ADDR_BITS), 0, 0, xoffs, yoffs, s, TW / 2 - xoffs, 3 * TH - yoffs, inv); + } + } + } + else + { + // reminder + if (yoffs) { + tile_p = get_tile_xy(x >> 3, (y >> 3) + 2); + + link_buffer(tile_p); + put_sprite_rc((uint8_t *)(tile_p->baddr & ADDR_BITS), xoffs, 0, TW / 2 - xoffs, yoffs, s, 0, 2 * TH - yoffs, inv); + if (xoffs) + { + tile_p++; + link_buffer(tile_p); + put_sprite_rc((uint8_t *)(tile_p->baddr & ADDR_BITS), 0, 0, xoffs, yoffs, s, TW / 2 - xoffs, 2 * TH - yoffs, inv); + } + } + } + } + else + { + // reminder + if (yoffs) { + tile_p = get_tile_xy(x >> 3, (y >> 3) + 1); + + link_buffer(tile_p); + put_sprite_rc((uint8_t *)(tile_p->baddr & ADDR_BITS), xoffs, 0, TW / 2 - xoffs, yoffs, s, 0, TH - yoffs, inv); + if (xoffs) + { + tile_p++; + link_buffer(tile_p); + put_sprite_rc((uint8_t *)(tile_p->baddr & ADDR_BITS), 0, 0, xoffs, yoffs, s, TW / 2 - xoffs, TH - yoffs, inv); + } + } + } + */ + + // *INDENT-OFF* + __asm; + + ; for xoffs and yoffs + push af + + ld ix, #0 + add ix, sp + + ld a, 2 + 4 (ix) + and #7 + srl a + ld 0 (ix), a + ; (x mod 8) / 2 -> xoffs + + ld a, 3 + 4 (ix) + and #7 + ld 1 (ix), a + ; (y mod 8) -> yoffs + + ld e, 2 + 4 (ix) + srl e + srl e + srl e + ld d, 3 + 4 (ix) + srl d + srl d + srl d + push de + ; x >> 3, y >> 3 + call _get_tile_xy + pop af + + ; *tile + push hl + ; fastcall, does not use ix + call _link_buffer + pop hl + push hl + + ld e, (hl) + inc hl + ld d, (hl) + res #7, d + ; de has baddr & ADDR_BITS + + ld b, 5 + 4 (ix) + ; inv + ld c, #0 + push bc + ; soy + ld b, #0 + push bc + inc sp + ; soy + ld c, 0 + 4 (ix) + ld b, 1 + 4 (ix) + push bc + ; *s + ; xoffs & yoffs are on the stack + ld a, #4 + sub 0 (ix) + ld c, a + ld a, #8 + sub 1 (ix) + ld b, a + push bc + ; TH / 2 - xoffs, TH - yoffs + ld c, 0 (ix) + ld b, 1 (ix) + push bc + ; xoffs, yoffs + push de + ; baddr + call _put_sprite_rc + + ld iy, #0 + add iy, sp + + ld a, 0 (ix) + or a + jr z, put_sprite4_xaligned0 + + ; *tile + ld l, 11 (iy) + ld h, 12 (iy) + + ld bc, #8 + add hl, bc + ; *tile + 1 + + push hl + ; fastcall, does not use ix or iy + call _link_buffer + pop hl + + ld e, (hl) + inc hl + ld d, (hl) + res #7, d + ; de has baddr & ADDR_BITS + + pop af + push de + ; save parameter + + ; update parameters that changed + xor a + ld 2 (iy), a + ; 0 + + ld c, 4 (iy) + ; TW / 2 - xoffs + + ld a, 0 (ix) + ld 4 (iy), a + ; xoffs + + ld 8 (iy), c + ; TW / 2 - xoffs + + call _put_sprite_rc + +put_sprite4_xaligned0: + + ld a, 4 + 4 (ix) + cp #1 + jp z, put_sprite4_reminder_th1 + + ; *tile + ld l, 11 (iy) + ld h, 12 (iy) + + ld bc, #(8 * 20) + add hl, bc + ; *tile + TMW + + push hl + ; fastcall, does not use ix or iy + call _link_buffer + pop hl + + ld e, (hl) + inc hl + ld d, (hl) + res #7, d + ; de has baddr & ADDR_BITS + + pop af + push de + ; save parameter + + ; update parameters + ld a, 0 (ix) + ld 2 (iy), a + ; xoffs + + xor a + ld 3 (iy), a + ld 8 (iy), a + ; 0 + + ld a, #4 + sub 0 (ix) + ld 4 (iy), a + ; TW / 2 - xoffs + + ld a, #8 + ld 5 (iy), a + ; TH + + ; a is 8 + sub 1 (ix) + ld 9 (iy), a + ; TH - yoffs + + call _put_sprite_rc + + ld a, 0 (ix) + or a + jr z, put_sprite4_xaligned1 + + ; *tile + ld l, 11 (iy) + ld h, 12 (iy) + + ld bc, #(8 * (20 + 1)) + add hl, bc + ; *tile + TMW + 1 + + push hl + ; fastcall, does not use ix or iy + call _link_buffer + pop hl + + ld e, (hl) + inc hl + ld d, (hl) + res #7, d + ; de has baddr & ADDR_BITS + + pop af + push de + ; save parameter + + ; update parameters that changed + xor a + ld 2 (iy), a + ; 0 + + ld c, 4 (iy) + ; TW / 2 - xoffs + + ld a, 0 (ix) + ld 4 (iy), a + ; xoffs + + ld 8 (iy), c + ; TW / 2 - xoffs + + call _put_sprite_rc + +put_sprite4_xaligned1: + + ld a, 4 + 4 (ix) + cp #2 + jp z, put_sprite4_reminder_th2 + + ; *tile + ld l, 11 (iy) + ld h, 12 (iy) + + ld bc, #(8 * 20 * 2) + add hl, bc + ; *tile + TMW * 2 + + push hl + ; fastcall, does not use ix or iy + call _link_buffer + pop hl + + ld e, (hl) + inc hl + ld d, (hl) + res #7, d + ; de has baddr & ADDR_BITS + + pop af + push de + ; save parameter + + ; update parameters + ld a, 0 (ix) + ld 2 (iy), a + ; xoffs + + xor a + ld 3 (iy), a + ld 8 (iy), a + ; 0 + + ld a, #4 + sub 0 (ix) + ld 4 (iy), a + ; TW / 2 - xoffs + + ld a, #8 + ld 5 (iy), a + ; TH + + sla a + ; TH * 2 + sub 1 (ix) + ld 9 (iy), a + ; 2 * TH - yoffs + + call _put_sprite_rc + + ld a, 0 (ix) + or a + jr z, put_sprite4_reminder + + ; *tile + ld l, 11 (iy) + ld h, 12 (iy) + + ld bc, #(8 * (20 * 2 + 1)) + add hl, bc + ; *tile + 2 * TMW + 1 + + push hl + ; fastcall, does not use ix or iy + call _link_buffer + pop hl + + ld e, (hl) + inc hl + ld d, (hl) + res #7, d + ; de has baddr & ADDR_BITS + + pop af + push de + ; save parameter + + ; update parameters that changed + xor a + ld 2 (iy), a + ; 0 + + ld c, 4 (iy) + ; TW / 2 - xoffs + + ld a, 0 (ix) + ld 4 (iy), a + ; xoffs + + ld 8 (iy), c + ; TW / 2 - xoffs + + call _put_sprite_rc + +put_sprite4_reminder: + ld a, 1 (ix) + or a + ; yoffs + jp z, put_sprite4_done + + ; *tile + ld l, 11 (iy) + ld h, 12 (iy) + + ld bc, #(8 * 20 * 3) + add hl, bc + ; *tile + TMW * 3 + + push hl + ; fastcall, does not use ix or iy + call _link_buffer + pop hl + + ld e, (hl) + inc hl + ld d, (hl) + res #7, d + ; de has baddr & ADDR_BITS + + pop af + push de + ; save parameter + + ; update parameters + ld a, 0 (ix) + ld 2 (iy), a + ; xoffs + + xor a + ld 3 (iy), a + ld 8 (iy), a + ; 0 + + ld a, #4 + sub 0 (ix) + ld 4 (iy), a + ; TW / 2 - xoffs + + ld a, 1 (ix) + ld 5 (iy), a + ; yoffs + + ld a, #(8 * 3) + sub 1 (ix) + ld 9 (iy), a + ; 3 * TH - yoffs + + call _put_sprite_rc + + ld a, 0 (ix) + or a + jp z, put_sprite4_done + + ; *tile + ld l, 11 (iy) + ld h, 12 (iy) + + ld bc, #(8 * (20 * 3 + 1)) + add hl, bc + ; *tile + 3 * TMW + 1 + + push hl + ; fastcall, does not use ix or iy + call _link_buffer + pop hl + + ld e, (hl) + inc hl + ld d, (hl) + res #7, d + ; de has baddr & ADDR_BITS + + pop af + push de + ; save parameter + + ; update parameters that changed + xor a + ld 2 (iy), a + ; 0 + + ld c, 4 (iy) + ; TW / 2 - xoffs + + ld a, 0 (ix) + ld 4 (iy), a + ; xoffs + + ld 8 (iy), c + ; TW / 2 - xoffs + + ld a, #(8 * 3) + sub 1 (ix) + ld 9 (iy), a + ; 3 * TH - yoffs + + call _put_sprite_rc + + jr put_sprite4_done + +put_sprite4_reminder_th2: + + ld a, 1 (ix) + or a + ; yoffs + jr z, put_sprite4_done + + ; *tile + ld l, 11 (iy) + ld h, 12 (iy) + + ld bc, #(8 * 20 * 2) + add hl, bc + ; *tile + TMW * 2 + + push hl + ; fastcall, does not use ix or iy + call _link_buffer + pop hl + + ld e, (hl) + inc hl + ld d, (hl) + res #7, d + ; de has baddr & ADDR_BITS + + pop af + push de + ; save parameter + + ; update parameters + ld a, 0 (ix) + ld 2 (iy), a + ; xoffs + + xor a + ld 3 (iy), a + ld 8 (iy), a + ; 0 + + ld a, #4 + sub 0 (ix) + ld 4 (iy), a + ; TW / 2 - xoffs + + ld a, 1 (ix) + ld 5 (iy), a + ; yoffs + + ld a, #(8 * 2) + sub 1 (ix) + ld 9 (iy), a + ; 2 * TH - yoffs + + call _put_sprite_rc + + ld a, 0 (ix) + or a + jr z, put_sprite4_done + + ; *tile + ld l, 11 (iy) + ld h, 12 (iy) + + ld bc, #(8 * (20 * 2 + 1)) + add hl, bc + ; *tile + 2 * TMW + 1 + + push hl + ; fastcall, does not use ix or iy + call _link_buffer + pop hl + + ld e, (hl) + inc hl + ld d, (hl) + res #7, d + ; de has baddr & ADDR_BITS + + pop af + push de + ; save parameter + + ; update parameters that changed + xor a + ld 2 (iy), a + ; 0 + + ld c, 4 (iy) + ; TW / 2 - xoffs + + ld a, 0 (ix) + ld 4 (iy), a + ; xoffs + + ld 8 (iy), c + ; TW / 2 - xoffs + + ld a, #(8 * 2) + sub 1 (ix) + ld 9 (iy), a + ; 2 * TH - yoffs + + call _put_sprite_rc + + put_sprite4_done: + ld hl, #15 + add hl, sp + ld sp, hl + + ret + +put_sprite4_reminder_th1: + + ld a, 1 (ix) + or a + ; yoffs + jr z, put_sprite4_done + + ; *tile + ld l, 11 (iy) + ld h, 12 (iy) + + ld bc, #(8 * 20 * 1) + add hl, bc + ; *tile + TMW * 1 + + push hl + ; fastcall, does not use ix or iy + call _link_buffer + pop hl + + ld e, (hl) + inc hl + ld d, (hl) + res #7, d + ; de has baddr & ADDR_BITS + + pop af + push de + ; save parameter + + ; update parameters + ld a, 0 (ix) + ld 2 (iy), a + ; xoffs + + xor a + ld 3 (iy), a + ld 8 (iy), a + ; 0 + + ld a, #4 + sub 0 (ix) + ld 4 (iy), a + ; TW / 2 - xoffs + + ld a, 1 (ix) + ld 5 (iy), a + ; yoffs + + ld a, #8 + sub 1 (ix) + ld 9 (iy), a + ; TH - yoffs + + call _put_sprite_rc + + ld a, 0 (ix) + or a + jr z, put_sprite4_done + + ; *tile + ld l, 11 (iy) + ld h, 12 (iy) + + ld bc, #(8 * (20 * 1 + 1)) + add hl, bc + ; *tile + 1 * TMW + 1 + + push hl + ; fastcall, does not use ix or iy + call _link_buffer + pop hl + + ld e, (hl) + inc hl + ld d, (hl) + res #7, d + ; de has baddr & ADDR_BITS + + pop af + push de + ; save parameter + + ; update parameters that changed + xor a + ld 2 (iy), a + ; 0 + + ld c, 4 (iy) + ; TW / 2 - xoffs + + ld a, 0 (ix) + ld 4 (iy), a + ; xoffs + + ld 8 (iy), c + ; TW / 2 - xoffs + + ld a, #8 + sub 1 (ix) + ld 9 (iy), a + ; TH - yoffs + + call _put_sprite_rc + + jp put_sprite4_done + + __endasm; + // *INDENT-ON* +} + +uint8_t tink, fink, bink; +uint8_t tfont[16]; + +void _set_text_ink(uint8_t c, uint8_t *t) +{ + *t = ((c & 1) << 7) | ((c & 1) << 6) | ((c & 4) << 3) | ((c & 4) << 2) + | ((c & 2) << 2) | ((c & 2) << 1) | ((c & 8) >> 2) | ((c & 8) >> 3); +} + +void set_text_ink(uint8_t c, uint8_t c2, uint8_t c3) +{ + _set_text_ink(c, &tink); + _set_text_ink(c2, &fink); + _set_text_ink(c3, &bink); +} + +#include <string.h> +void cpc_PutSp(char *sprite, char height, char width, int address); + +void tint_text() +{ + // *INDENT-OFF* + __asm; + ld de, #_tfont + + ld hl, #_tink + ld c, (hl) + + ld hl, #_fink + ld a, (hl) + + ld hl, #_bink + ld h, (hl) + ld l, a + + ld b, #6 +tint_loop1: + ld a, (de) + and c + ld (de), a + inc de + + djnz tint_loop1 + + ld b, #6 +tint_loop2: + ld a, (de) + and l + ld (de), a + inc de + + djnz tint_loop2 + + ld b, #4 +tint_loop3: + ld a, (de) + and h + ld (de), a + inc de + + djnz tint_loop3 + + __endasm; + // *INDENT-ON* +} + +#pragma save +#pragma disable_warning 85 +void put_text(const char *s, uint8_t x, uint8_t y) +{ + /* + while(*s) + { + memcpy(tfont, spfont[*s - 31], 16); + tint_text(); + cpc_PutSp(tfont, 8, 2, screen_addr(x, y)); + x += 2; + s++; + } + */ + + // *INDENT-OFF* + __asm; + ld hl, #2 + add hl, sp + + ld e, (hl) + inc hl + ld d, (hl) ; s + inc hl + ld b, (hl) ; x + inc hl + ld c, (hl) ; y + +put_text_loop: + push de + push bc + + ld hl, #16 + push hl + + ld a, (de) + sub #31 + ld l, a ; h is 0 + add hl, hl + add hl, hl + add hl, hl + add hl, hl + ld bc, #_spfont + add hl, bc + push hl + + ld hl, #_tfont + push hl + + call _memcpy + pop af + pop af + pop af + + call _tint_text + + pop hl + push hl + + ld a, h + ld h, #0 + push hl + ld l, a + push hl + + call _screen_addr + pop af + pop af + + push hl + + ld hl, #0x0208 + push hl + + ld hl, #_tfont + push hl + + call _cpc_PutSp + pop af + pop af + pop af + + pop bc + pop de + + inc b + inc b + + inc de + ld a, (de) + or #0 + jr nz, put_text_loop + + __endasm; + // *INDENT-ON* +} +#pragma restore + +#pragma save +#pragma disable_warning 85 +void fill_screen(const uint8_t *t) __z88dk_fastcall +{ + // *INDENT-OFF* + __asm; + ex de, hl + + ld hl, #_tiles + ld bc, #(TMW * TMH) + +fill_screen_loop: + inc hl + inc hl + + ld (hl), e + inc hl + ld (hl), d + inc hl + + inc hl + inc hl + inc hl + inc hl + + dec bc + ld a, c + or b + jr nz, fill_screen_loop + + jp _invalidate_screen + __endasm; + // *INDENT-ON* +} +#pragma restore + +uint8_t abs_sub(uint8_t a, uint8_t b) +{ + /* + if (a > b) + return (a - b); + else + return (b - a); + */ + // *INDENT-OFF* + __asm; + ld hl, #2 + add hl, sp + ld b, (hl) + inc hl + ld c, (hl) + ld a, b + sub c + jr c, b_minus_a + ld l, a + ret +b_minus_a: + ld a, c + sub b + ld l, a + __endasm; + // *INDENT-ON* +} + +// EOF diff --git a/src/splib.h b/src/splib.h new file mode 100644 index 0000000..0fc4f1c --- /dev/null +++ b/src/splib.h @@ -0,0 +1,82 @@ +/* + Kitsune's Curse + Copyright (C) 2020-2023 Juan J. Martinez <jjm@usebox.net> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef _SPLIB_H +#define _SPLIB_H + +#include <stdint.h> + +// this is 160x160; 8x8 pixels tiles +#define TMW 20 +#define TMH 20 + +#define TW 8 +#define TH 8 + +// All the buffer must be under 0x8000 +#define BUFF_ADDR 0x0100 +// CONFIGURE ** + +#define SCREEN_ADDR(x, y) ((int)(0xc000 + x + ((y / 8) * 80) + ((y % 8) * 2048))) + +#define DIRTY_BIT 0x8000 +#define ADDR_BITS 0x7fff + +struct st_tile +{ + uint16_t baddr; + const uint8_t *t; + uint16_t saddr; + struct st_tile *n; +}; + +void init_tiles(); +void update_screen(); +void validate_screen(); +void invalidate_screen(); + +struct st_tile *get_tile_xy(uint8_t x, uint8_t y); + +void invalidate_tile(struct st_tile *st) __z88dk_fastcall; +uint8_t is_invalid_tile(struct st_tile *st) __z88dk_fastcall; +uint8_t is_invalid_tile2(struct st_tile *st) __z88dk_fastcall; + +void erase_tile(struct st_tile *st) __z88dk_fastcall; +void link_buffer(struct st_tile *st) __z88dk_fastcall; +void link_buffer_xy(uint8_t x, uint8_t y); + +void put_tile(const uint8_t *t, struct st_tile *st); +void put_sprite4(const uint8_t *s, uint8_t x, uint8_t y, uint8_t th, uint8_t inv); +void erase_sprite(uint8_t x, uint8_t y, uint8_t th); + +void fill_screen(const uint8_t *t) __z88dk_fastcall; + +// misc +void clear_screen(); +void wait_vsync(); +void set_hw_border(uint8_t c) __z88dk_fastcall; +void set_hw_ink(uint8_t ink, uint8_t c); +void pad_numbers(uint8_t *s, uint8_t limit, uint16_t number); +uint16_t screen_addr(uint16_t x, uint16_t y); + +uint8_t abs_sub(uint8_t a, uint8_t b); + +// require spfont, 63 chars (starting on 31) +void set_text_ink(uint8_t c, uint8_t c2, uint8_t c3); +void put_text(const char *s, uint8_t x, uint8_t y); + +#endif // _SPLIB_H diff --git a/src/turboload.s b/src/turboload.s new file mode 100644 index 0000000..0b6b984 --- /dev/null +++ b/src/turboload.s @@ -0,0 +1,250 @@ +;; Modified Topo soft loader +;; +;; Multi-colour bars in the border, aka "Spanish" style Spectrum variant loader. +;; +;; This loader uses standard Spectrum ROM timings. In fact it is essentially a CPC version of the +;; Spectrum ROM loader itself. +;; +;; +;; Loader form on cassette: +;; Pilot tone (min 256 pulses) +;; Sync pulse +;; 1 byte sync byte (on spectrum 0 for header and &ff for data) +;; n bytes of data (defined by DE register) +;; 1 byte parity checksum +;; +;; Original had the following bugs: +;; 1. bit 7 of R register had to be 1, otherwise it could change ram/rom configuration while loading +;; 2. flags were not returned, so you didn't know if the data was read incorrectly or not +;; +;; It also had extra code that was unnecessary: +;; 1. turning cassette motor on/off was complex, when in previous instructions they had turned it on anyway. +;; 2. code for changing border colour presumably on a read error. but didn't seem to be used. +;; +;; +;; Entry conditions: +;; +;; IX = start +;; DE = length (D must not be &ff) +;; A = sync byte expected +;; +;; Interrupts must be disabled +;; +;; Exit Conditions: +;; Alternative register set is corrupted. +;; +;; carry clear - load ok +;; carry set, zero set - time up +;; carry set, zero reset - if esc pressed +;; +;; Use 2CDT to write Spectrum ROM blocks to a CDT. Note that at this time it will only write a sync byte of &ff. + +_turboload: + +inc d ;; reset the zero flag (D cannot hold &ff) +ex af,af' ;; A register holds sync byte. +dec d ;; restore D +exx +;; we need B' to be so we can write to gate-array i/o port for colour change when loading + +ld bc,#0x7f00+#0x10 ;; Gate-Array + border +out (c),c ;; select pen index to change while loading (&10 is border) +ld c,#0x54 ;; set to black +out (c),c +exx + +ld bc,#0xf40e ;; select AY register 14 (for reading keyboard) +out (c),c + +ld bc,#0xf600+#0xc0+#0x10 ;; "AY write register" and enable tape motor +out (c),c + +ld c,#0x10 ;; "AY inactive" and enable tape motor (register is latched into AY) +out (c),c + +ld bc,#0xf792 ;; set PPI port A to read (so we can read keyboard data) +out (c),c ;; this will also write 0 to port C and port A on CPC. + +ld bc,#0xf600+#0x40+#0x10+#0x8 ;; tape motor enable, "AY read register", select keyboard row 8 + ;; (keys on this row: z, caps lock, a, tab, q, esc, 2, 1) + ;; we are only interested in ESC +out (c),c + +;; make an initial read of cassette input +ld a,#0xf5 ;; PPI port B +in a,(#0) ;; read port (tape read etc) +and #0x80 ;; isolate tape read data + +ld c,a +cp a ;; set the zero flag +l8107: +ret nz ;; returns if esc key is pressed (was RET NZ) +l8108: +call l817b +jr nc,l8107 + +;; the wait is meant to be almost one second +ld hl,#0x415 +l8110: +djnz l8110 +dec hl +ld a,h +or l +jr nz,l8110 +;; continue if only two edges are found within this allowed time period +call l8177 +jr nc,l8107 + +;; only accept leader signal +l811c: +ld b,#0x9c +call l8177 +jr nc,l8107 +ld a,#0xb9 +cp b +jr nc,l8108 +inc h +jr nz,l811c + +;; on/off parts of sync pulse +l812b: +ld b,#0xc9 +call l817b +jr nc,l8107 +ld a,b +cp #0xd1 +jr nc,l812b +l8137: +call l817b +ret nc + +ld h,#0 ;; parity matching byte (checksum) +ld b,#0xb0 ;; timing constant for flag byte and data +jr l815d + +l8145: +ex af,af' ;; fetch the flags +jr nz,l814d ;; jump if we are handling first byte +;; L = data byte read from cassette +;; store to RAM +ld 0(ix),l +jr l8157 + +l814d: +rr c ;; keep carry in safe place + ;; NOTE: Bit 7 is cassette read previous state + ;; We need to do a right shift so that cassette read moves into bit 6, + ;; our stored carry then goes into bit 7 +xor l ;; check sync byte is as we expect +ret nz + +ld a,c ;; restore carry flag now +rla ;; bit 7 goes into carry restoring it, bit 6 goes back to bit 7 to restore tape input value +ld c,a +inc de ;; increase counter to compensate for it's decrease +jr l8159 + +l8157: +inc ix ;; increase destination +l8159: +dec de ;; decrease counter +ex af,af' ;; save the flags + +ld b,#0xb2 ;; timing constant +l815d: +ld l,#1 ;; marker bit (defines number of bits to read, and finally becomes value read) +l815f: +call l8177 +ret nc + +ld a,#0xc3 ;; compare the length against approx 2,400T states, resetting the carry flag for a '0' and setting it for a '1' +cp b +rl l ;; include the new bit in the register +ld b,#0xb0 ;; set timing constant for next bit +jr nc,l815f + +;; L = data byte read +;; combine with checksum +ld a,h +xor l +ld h,a + +;; DE = count +ld a,d +or e +jr nz,l8145 + +exx +ld c,#0x54 ;; make border black +out (c),c +exx + +ld bc,#0xf782 ;; set PPI port A for output +out (c),c +ld bc,#0xf600 ;; tape motor off, "AY inactive" +out (c),c + +;; H = checksum byte +;; H = 0 means checksum ok +ld a,h +cp #1 +;; return with carry flag set if the result is good +;; carry clear otherwise +ret + + +;;------------------------------------------------------------ +l8177: +call l817b ;; in effect call ld-edge-1 twice returning in between if there is an error +ret nc + +;; wait 358T states before entering sampling loop +l817b: +ld a,#0x16 ;; [2] +l817d: +dec a ;; [1] +jr nz,l817d ;; ([3]+[1])*&15+[2] + +and a +l8181: +inc b ;; count each pass +ret z ;; return carry reset and zero set if time up. + +;; read keyboard +ld a,#0xf4 ;; PPI port A +in a,(#0) ;; read port and read keyboard through AY register 14 +and #0x04 ;; isolate state of bit 2 (ESC key) + ;; bit will be 0 if key is pressed +xor #0x04 ;; has key been pressed? +ret nz ;; quit (carry reset, zero reset) + +ld a,#0xf5 ;; PPI port B +in a,(#0) ;; read port (tape read etc) +xor c +and #0x80 ;; isolate tape read +jr z,l8181 +ld a,c ;; effectively toggle bit 7 of C +cpl ;; which is the tape read state we want to see next +ld c,a + +exx ;; swap to alternative register set so that we can change colour +;; this sets colour and gives us the multi colour border +;ld a,r ;; get R register +;and #0x1f ;; ensure it is in range of colour value + +nop +nop +nop +turbo_col: +and #4 + +or #0x40 ;; bit 7 = 0, bit 6 = 1 +out (c),a ;; write colour value +nop ;; this is to ensure the number of cycles is the same + ;; compared to the replaced instructions + ;; (timings from toposoft loader) +nop +exx +scf +ret + |