commit 3ea8481a271257a2ed5538db5f5c5677111a791a Author: Trevor Boddy Date: Thu Jun 17 17:59:18 2021 -0400 here you go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea4259c --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +.DS_Store +**/.DS_Store +Thumbs.db +**/Thumbs.db +out.bin +out.elf +symbol.txt +blastem/ +boot/rom_head.bin +boot/sega.o +res/resources.o +res/resources.h +dist/ \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f9f0227 --- /dev/null +++ b/Makefile @@ -0,0 +1,145 @@ +# Sample Makefile for Marsdev (SGDK version) + +# Default paths, can be overridden by setting MARSDEV before calling make +MARSDEV ?= ${HOME}/mars +MARSBIN = $(MARSDEV)/m68k-elf/bin +TOOLSBIN = $(MARSDEV)/bin + +# GCC and Binutils +CC = $(MARSBIN)/m68k-elf-gcc +CXX = $(MARSBIN)/m68k-elf-g++ +AS = $(MARSBIN)/m68k-elf-as +LD = $(MARSBIN)/m68k-elf-ld +NM = $(MARSBIN)/m68k-elf-nm +OBJC = $(MARSBIN)/m68k-elf-objcopy +# SGDK Tools +BINTOS = $(TOOLSBIN)/bintos +PCMTORAW = $(TOOLSBIN)/pcmtoraw +RESCOMP = $(TOOLSBIN)/rescomp +WAVTORAW = $(TOOLSBIN)/wavtoraw +XGMTOOL = $(TOOLSBIN)/xgmtool +# Use rescomp jar for SGDK > 1.33 +ifneq ("$(wildcard $(TOOLSBIN)/rescomp.jar)","") + RESCOMP = java -jar $(TOOLSBIN)/rescomp.jar + WAVTORAW = @echo "wavtoraw no longer available!" ## + PCMTORAW = @echo "pcmtoraw no longer available!" ## +endif + +# Some files needed are in a versioned directory +GCC_VER := $(shell $(CC) -dumpversion) + +# Need the LTO plugin so NM can dump our symbol table +PLUGIN = $(MARSDEV)/m68k-elf/libexec/gcc/m68k-elf/$(GCC_VER) +LTO_SO = liblto_plugin.so +ifeq ($(OS),Windows_NT) + LTO_SO = liblto_plugin-0.dll +endif + +# Includes: Local + GCC + SGDK + Newlib +INCS = -Isrc -Ires -Iinc +INCS += -I$(MARSDEV)/m68k-elf/lib/gcc/m68k-elf/$(GCC_VER)/include +INCS += -I$(MARSDEV)/m68k-elf/include +INCS += -I$(MARSDEV)/m68k-elf/m68k-elf/include + +# Libraries: GCC + Newlib (SGDK libs are with release/debug targets) +# If you plan on using Newlib, uncomment the line with -lnosys +LIBS = -L$(MARSDEV)/m68k-elf/lib/gcc/m68k-elf/$(GCC_VER) -lgcc +#LIBS += -L$(MARSDEV)/m68k-elf/m68k-elf/lib -lnosys + +# Force libgcc math routines to be available at link time +LIBS += -u __modsi3 -u __divsi3 -u __mulsi3 -u __umodsi3 -u __udivsi3 -u __umulsi3 + +# Any C or C++ standard should be fine here as long as GCC support it +CCFLAGS = -m68000 -Wall -Wextra -std=c99 -ffreestanding -fcommon +CXXFLAGS = -m68000 -Wall -Wextra -std=c++17 -ffreestanding + +# Extra options set by debug or release target +OPTIONS = +ASFLAGS = -m68000 --register-prefix-optional +LDFLAGS = -T $(MARSDEV)/ldscripts/sgdk.ld -nostdlib -fcommon + +RESS = $(wildcard res/*.res) +CS = $(wildcard src/*.c) +CPPS = $(wildcard src/*.cpp) +SS = $(wildcard src/*.s) + +OBJS = $(RESS:.res=.o) +OBJS += $(CS:.c=.o) +OBJS += $(CPPS:.cpp=.o) +OBJS += $(SS:.s=.o) + +ASMO = $(RESS:.res=.o) +ASMO += $(CS:%.c=asmout/%.s) + +.SECONDARY: out.elf + +.PHONY: all release asm debug + +all: release + +release: OPTIONS = -O3 -fno-web -fno-gcse -fno-unit-at-a-time -fomit-frame-pointer +release: OPTIONS += -fshort-enums -flto -fuse-linker-plugin +release: LIBS += -L$(MARSDEV)/m68k-elf/lib -lmd +release: out.bin symbol.txt + +asm: OPTIONS = -O3 -fno-web -fno-gcse -fno-unit-at-a-time -fomit-frame-pointer +asm: OPTIONS += -fshort-enums +asm: LIBS += -L$(MARSDEV)/m68k-elf/lib -lmd +asm: asm-dir $(ASMO) + +# Gens-KMod, BlastEm and UMDK support GDB tracing, enabled by this target +debug: OPTIONS = -g -Og -DDEBUG -DKDEBUG -fno-web -fno-gcse -fno-unit-at-a-time -fshort-enums +debug: LIBS += -L$(MARSDEV)/m68k-elf/lib -lmd-debug +debug: out.bin symbol.txt + +# This generates a symbol table that is very helpful in debugging crashes, +# even with an optimized release build! +# Cross reference symbol.txt with the addresses displayed in the crash handler +symbol.txt: out.bin + $(NM) --plugin=$(PLUGIN)/$(LTO_SO) -n out.elf > symbol.txt + +boot/sega.o: boot/rom_head.bin + @echo "AS $<" + @$(AS) $(ASFLAGS) boot/sega.s -o $@ + +boot/rom_head.bin: boot/rom_head.c + @echo "CC $<" + @$(CC) $(CFLAGS) $(INCS) -nostdlib -Wl,--oformat=binary $< -o $@ + +%.bin: %.elf + @echo "Stripping ELF header..." + @$(OBJC) -O binary $< temp.bin + @dd if=temp.bin of=$@ bs=8192 conv=sync + @rm -f temp.bin + +%.elf: boot/sega.o $(OBJS) + $(CC) -o $@ $(LDFLAGS) boot/sega.o $(OBJS) $(LIBS) + +%.o: %.c + @echo "CC $<" + @$(CC) $(CCFLAGS) $(OPTIONS) $(INCS) -c $< -o $@ + +%.o: %.cpp + @echo "CXX $<" + @$(CXX) $(CXXFLAGS) $(OPTIONS) $(INCS) -c $< -o $@ + +%.o: %.s + @echo "AS $<" + @$(AS) $(ASFLAGS) $< -o $@ + +%.s: %.res + $(RESCOMP) $< $@ + +# For asm target +asm-dir: + mkdir -p asmout/src + +asmout/%.s: %.c + $(CC) $(CCFLAGS) $(OPTIONS) $(INCS) -S $< -o $@ + +.PHONY: clean + +clean: + rm -f $(OBJS) out.bin out.elf symbol.txt + rm -f boot/sega.o boot/rom_head.o boot/rom_head.bin + rm -rf asmout diff --git a/README.md b/README.md new file mode 100644 index 0000000..c4283d6 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +**sweetsdigger** + +touhou pride jam 3 entry\ +arcade thing for sega genesis influenced by jeff minter's gridrunner and ZUN's highly responsive to prayers + +written with [sgdk](https://github.com/Stephane-D/SGDK)\ +builds with [marsdev](https://github.com/andwn/marsdev) on mac and linux diff --git a/boot/rom_head.c b/boot/rom_head.c new file mode 100644 index 0000000..5c3c47f --- /dev/null +++ b/boot/rom_head.c @@ -0,0 +1,43 @@ +#include "types.h" + +__attribute__((externally_visible)) +const struct +{ + char console[16]; /* Console Name (16) */ + char copyright[16]; /* Copyright Information (16) */ + char title_local[48]; /* Domestic Name (48) */ + char title_int[48]; /* Overseas Name (48) */ + char serial[14]; /* Serial Number (2, 12) */ + u16 checksum; /* Checksum (2) */ + char IOSupport[16]; /* I/O Support (16) */ + u32 rom_start; /* ROM Start Address (4) */ + u32 rom_end; /* ROM End Address (4) */ + u32 ram_start; /* Start of Backup RAM (4) */ + u32 ram_end; /* End of Backup RAM (4) */ + char sram_sig[2]; /* "RA" for save ram (2) */ + u16 sram_type; /* 0xF820 for save ram on odd bytes (2) */ + u32 sram_start; /* SRAM start address - normally 0x200001 (4) */ + u32 sram_end; /* SRAM end address - start + 2*sram_size (4) */ + char modem_support[12]; /* Modem Support (24) */ + char notes[40]; /* Memo (40) */ + char region[16]; /* Country Support (16) */ +} rom_header = { + "SEGA MEGA DRIVE ", + "TBODDY ", + "SWEETSDIGGER ", + "SWEETSDIGGER ", + "GM 00000000-00", + 0x0000, + "JD ", + 0x00000000, + 0x00100000, + 0x00FF0000, + 0x00FFFFFF, + " ", + 0x0000, + 0x00200000, + 0x002001FF, + " ", + "SWEETSDIGGER ", + "JUE " +}; diff --git a/boot/sega.s b/boot/sega.s new file mode 100644 index 0000000..56302b8 --- /dev/null +++ b/boot/sega.s @@ -0,0 +1,462 @@ +.section .text.keepboot + +*------------------------------------------------------- +* +* Sega startup code for the GNU Assembler +* Translated from: +* Sega startup code for the Sozobon C compiler +* Written by Paul W. Lee +* Modified by Charles Coty +* Modified by Stephane Dallongeville +* +*------------------------------------------------------- + + .globl _hard_reset + + .org 0x00000000 + +_Start_Of_Rom: +_Vecteurs_68K: + dc.l 0x00000000 /* Stack address */ + dc.l _Entry_Point /* Program start address */ + dc.l _Bus_Error + dc.l _Address_Error + dc.l _Illegal_Instruction + dc.l _Zero_Divide + dc.l _Chk_Instruction + dc.l _Trapv_Instruction + dc.l _Privilege_Violation + dc.l _Trace + dc.l _Line_1010_Emulation + dc.l _Line_1111_Emulation + dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception + dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception + dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception + dc.l _Error_Exception, _INT, _EXTINT, _INT + dc.l _HINT + dc.l _INT + dc.l _VINT + dc.l _INT + dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT + dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT + dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT + dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT + + .incbin "boot/rom_head.bin" + +_Entry_Point: + move #0x2700,%sr + tst.l 0xa10008 + bne.s SkipJoyDetect + + tst.w 0xa1000c + +SkipJoyDetect: + bne.s SkipSetup + + lea Table,%a5 + movem.w (%a5)+,%d5-%d7 + movem.l (%a5)+,%a0-%a4 +* Check Version Number + move.b -0x10ff(%a1),%d0 + andi.b #0x0f,%d0 + beq.s WrongVersion + +* Sega Security Code (SEGA) + move.l #0x53454741,0x2f00(%a1) +WrongVersion: + move.w (%a4),%d0 + moveq #0x00,%d0 + movea.l %d0,%a6 + move %a6,%usp + move.w %d7,(%a1) + move.w %d7,(%a2) + +* Jump to initialisation process now... + + jmp _start_entry + +SkipSetup: + jmp _reset_entry + + +Table: + dc.w 0x8000,0x3fff,0x0100 + dc.l 0xA00000,0xA11100,0xA11200,0xC00000,0xC00004 + + +*------------------------------------------------ +* +* interrupt functions +* +*------------------------------------------------ + +registersDump: + move.l %d0,registerState+0 + move.l %d1,registerState+4 + move.l %d2,registerState+8 + move.l %d3,registerState+12 + move.l %d4,registerState+16 + move.l %d5,registerState+20 + move.l %d6,registerState+24 + move.l %d7,registerState+28 + move.l %a0,registerState+32 + move.l %a1,registerState+36 + move.l %a2,registerState+40 + move.l %a3,registerState+44 + move.l %a4,registerState+48 + move.l %a5,registerState+52 + move.l %a6,registerState+56 + move.l %a7,registerState+60 + rts + +busAddressErrorDump: + move.w 4(%sp),ext1State + move.l 6(%sp),addrState + move.w 10(%sp),ext2State + move.w 12(%sp),srState + move.l 14(%sp),pcState + jmp registersDump + +exception4WDump: + move.w 4(%sp),srState + move.l 6(%sp),pcState + move.w 10(%sp),ext1State + jmp registersDump + +exceptionDump: + move.w 4(%sp),srState + move.l 6(%sp),pcState + jmp registersDump + + +_Bus_Error: + jsr busAddressErrorDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l busErrorCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Address_Error: + jsr busAddressErrorDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l addressErrorCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Illegal_Instruction: + jsr exception4WDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l illegalInstCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Zero_Divide: + jsr exceptionDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l zeroDivideCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Chk_Instruction: + jsr exception4WDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l chkInstCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Trapv_Instruction: + jsr exception4WDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l trapvInstCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Privilege_Violation: + jsr exceptionDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l privilegeViolationCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Trace: + jsr exceptionDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l traceCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Line_1010_Emulation: +_Line_1111_Emulation: + jsr exceptionDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l line1x1xCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Error_Exception: + jsr exceptionDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l errorExceptionCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_INT: + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l intCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_EXTINT: + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l eintCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_HINT: + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l hintCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_VINT: + movem.l %d0-%d1/%a0-%a1,-(%sp) + ori.w #0x0001, intTrace /* in V-Int */ + addq.l #1, vtimer /* increment frame counter (more a vint counter) */ + btst #3, VBlankProcess+1 /* PROCESS_XGM_TASK ? (use VBlankProcess+1 as btst is a byte operation) */ + beq.s _no_xgm_task + + jsr XGM_doVBlankProcess /* do XGM vblank task */ + +_no_xgm_task: + btst #1, VBlankProcess+1 /* PROCESS_BITMAP_TASK ? (use VBlankProcess+1 as btst is a byte operation) */ + beq.s _no_bmp_task + + jsr BMP_doVBlankProcess /* do BMP vblank task */ + +_no_bmp_task: + move.l vintCB, %a0 /* load user callback */ + jsr (%a0) /* call user callback */ + andi.w #0xFFFE, intTrace /* out V-Int */ + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + + +*------------------------------------------------ +* +* Copyright (c) 1988 by Sozobon, Limited. Author: Johann Ruegg +* +* Permission is granted to anyone to use this software for any purpose +* on any computer system, and to redistribute it freely, with the +* following restrictions: +* 1) No charge may be made other than reasonable charges for reproduction. +* 2) Modified versions must be clearly marked as such. +* 3) The authors are not responsible for any harmful consequences +* of using this software, even if they result from defects in it. +* +*------------------------------------------------ + +ldiv: + move.l 4(%a7),%d0 + bpl ld1 + neg.l %d0 +ld1: + move.l 8(%a7),%d1 + bpl ld2 + neg.l %d1 + eor.b #0x80,4(%a7) +ld2: + bsr i_ldiv /* d0 = d0/d1 */ + tst.b 4(%a7) + bpl ld3 + neg.l %d0 +ld3: + rts + +lmul: + move.l 4(%a7),%d0 + bpl lm1 + neg.l %d0 +lm1: + move.l 8(%a7),%d1 + bpl lm2 + neg.l %d1 + eor.b #0x80,4(%a7) +lm2: + bsr i_lmul /* d0 = d0*d1 */ + tst.b 4(%a7) + bpl lm3 + neg.l %d0 +lm3: + rts + +lrem: + move.l 4(%a7),%d0 + bpl lr1 + neg.l %d0 +lr1: + move.l 8(%a7),%d1 + bpl lr2 + neg.l %d1 +lr2: + bsr i_ldiv /* d1 = d0%d1 */ + move.l %d1,%d0 + tst.b 4(%a7) + bpl lr3 + neg.l %d0 +lr3: + rts + +ldivu: + move.l 4(%a7),%d0 + move.l 8(%a7),%d1 + bsr i_ldiv + rts + +lmulu: + move.l 4(%a7),%d0 + move.l 8(%a7),%d1 + bsr i_lmul + rts + +lremu: + move.l 4(%a7),%d0 + move.l 8(%a7),%d1 + bsr i_ldiv + move.l %d1,%d0 + rts +* +* A in d0, B in d1, return A*B in d0 +* +i_lmul: + move.l %d3,%a2 /* save d3 */ + move.w %d1,%d2 + mulu %d0,%d2 /* d2 = Al * Bl */ + + move.l %d1,%d3 + swap %d3 + mulu %d0,%d3 /* d3 = Al * Bh */ + + swap %d0 + mulu %d1,%d0 /* d0 = Ah * Bl */ + + add.l %d3,%d0 /* d0 = (Ah*Bl + Al*Bh) */ + swap %d0 + clr.w %d0 /* d0 = (Ah*Bl + Al*Bh) << 16 */ + + add.l %d2,%d0 /* d0 = A*B */ + move.l %a2,%d3 /* restore d3 */ + rts +* +*A in d0, B in d1, return A/B in d0, A%B in d1 +* +i_ldiv: + tst.l %d1 + bne nz1 + +* divide by zero +* divu #0,%d0 /* cause trap */ + move.l #0x80000000,%d0 + move.l %d0,%d1 + rts +nz1: + move.l %d3,%a2 /* save d3 */ + cmp.l %d1,%d0 + bhi norm + beq is1 +* AB and B is not 0 +norm: + cmp.l #1,%d1 + bne not1 +* B==1, so ret A, rem 0 + clr.l %d1 + move.l %a2,%d3 /* restore d3 */ + rts +* check for A short (implies B short also) +not1: + cmp.l #0xffff,%d0 + bhi slow +* A short and B short -- use 'divu' + divu %d1,%d0 /* d0 = REM:ANS */ + swap %d0 /* d0 = ANS:REM */ + clr.l %d1 + move.w %d0,%d1 /* d1 = REM */ + clr.w %d0 + swap %d0 + move.l %a2,%d3 /* restore d3 */ + rts +* check for B short +slow: + cmp.l #0xffff,%d1 + bhi slower +* A long and B short -- use special stuff from gnu + move.l %d0,%d2 + clr.w %d2 + swap %d2 + divu %d1,%d2 /* d2 = REM:ANS of Ahi/B */ + clr.l %d3 + move.w %d2,%d3 /* d3 = Ahi/B */ + swap %d3 + + move.w %d0,%d2 /* d2 = REM << 16 + Alo */ + divu %d1,%d2 /* d2 = REM:ANS of stuff/B */ + + move.l %d2,%d1 + clr.w %d1 + swap %d1 /* d1 = REM */ + + clr.l %d0 + move.w %d2,%d0 + add.l %d3,%d0 /* d0 = ANS */ + move.l %a2,%d3 /* restore d3 */ + rts +* A>B, B > 1 +slower: + move.l #1,%d2 + clr.l %d3 +moreadj: + cmp.l %d0,%d1 + bhs adj + add.l %d2,%d2 + add.l %d1,%d1 + bpl moreadj +* we shifted B until its >A or sign bit set +* we shifted #1 (d2) along with it +adj: + cmp.l %d0,%d1 + bhi ltuns + or.l %d2,%d3 + sub.l %d1,%d0 +ltuns: + lsr.l #1,%d1 + lsr.l #1,%d2 + bne adj +* d3=answer, d0=rem + move.l %d0,%d1 + move.l %d3,%d0 + move.l %a2,%d3 /* restore d3 */ + rts diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..d0c2e7c --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +rm res/resources.o res/resources.h out.bin out.elf symbol.txt +make +./blastem/blastem out.bin \ No newline at end of file diff --git a/compile.sh b/compile.sh new file mode 100755 index 0000000..6e24737 --- /dev/null +++ b/compile.sh @@ -0,0 +1,2 @@ +rm out.bin out.elf symbol.txt +make \ No newline at end of file diff --git a/itchcover.png b/itchcover.png new file mode 100644 index 0000000..9509844 Binary files /dev/null and b/itchcover.png differ diff --git a/itchcover.psd b/itchcover.psd new file mode 100644 index 0000000..fec50f3 Binary files /dev/null and b/itchcover.psd differ diff --git a/res/bg/ground1.png b/res/bg/ground1.png new file mode 100644 index 0000000..57f32f8 Binary files /dev/null and b/res/bg/ground1.png differ diff --git a/res/bg/ground2.png b/res/bg/ground2.png new file mode 100644 index 0000000..a024545 Binary files /dev/null and b/res/bg/ground2.png differ diff --git a/res/bg/ground3.png b/res/bg/ground3.png new file mode 100644 index 0000000..c1e62ef Binary files /dev/null and b/res/bg/ground3.png differ diff --git a/res/bg/ground4.png b/res/bg/ground4.png new file mode 100644 index 0000000..2652ac6 Binary files /dev/null and b/res/bg/ground4.png differ diff --git a/res/bg/ground5.png b/res/bg/ground5.png new file mode 100644 index 0000000..33026e1 Binary files /dev/null and b/res/bg/ground5.png differ diff --git a/res/bg/ground6.png b/res/bg/ground6.png new file mode 100644 index 0000000..c7fc640 Binary files /dev/null and b/res/bg/ground6.png differ diff --git a/res/bg/ground7.png b/res/bg/ground7.png new file mode 100644 index 0000000..78e337e Binary files /dev/null and b/res/bg/ground7.png differ diff --git a/res/bg/ground8.png b/res/bg/ground8.png new file mode 100644 index 0000000..cb16b34 Binary files /dev/null and b/res/bg/ground8.png differ diff --git a/res/bgm/stage1.vgm b/res/bgm/stage1.vgm new file mode 100644 index 0000000..0eb92fa Binary files /dev/null and b/res/bgm/stage1.vgm differ diff --git a/res/bgm/stage2.vgm b/res/bgm/stage2.vgm new file mode 100644 index 0000000..4d295c7 Binary files /dev/null and b/res/bgm/stage2.vgm differ diff --git a/res/bgm/start.vgm b/res/bgm/start.vgm new file mode 100644 index 0000000..71b80b9 Binary files /dev/null and b/res/bgm/start.vgm differ diff --git a/res/bullets/bigblue.png b/res/bullets/bigblue.png new file mode 100644 index 0000000..5ec0b34 Binary files /dev/null and b/res/bullets/bigblue.png differ diff --git a/res/bullets/biggreen.png b/res/bullets/biggreen.png new file mode 100644 index 0000000..e5b8866 Binary files /dev/null and b/res/bullets/biggreen.png differ diff --git a/res/bullets/bigpink.png b/res/bullets/bigpink.png new file mode 100644 index 0000000..aabed77 Binary files /dev/null and b/res/bullets/bigpink.png differ diff --git a/res/bullets/bigred.png b/res/bullets/bigred.png new file mode 100644 index 0000000..17cd758 Binary files /dev/null and b/res/bullets/bigred.png differ diff --git a/res/bullets/bigyellow.png b/res/bullets/bigyellow.png new file mode 100644 index 0000000..1f3f8d0 Binary files /dev/null and b/res/bullets/bigyellow.png differ diff --git a/res/bullets/smallblue.png b/res/bullets/smallblue.png new file mode 100644 index 0000000..468a964 Binary files /dev/null and b/res/bullets/smallblue.png differ diff --git a/res/bullets/smallgreen.png b/res/bullets/smallgreen.png new file mode 100644 index 0000000..9f12015 Binary files /dev/null and b/res/bullets/smallgreen.png differ diff --git a/res/bullets/smallpink.png b/res/bullets/smallpink.png new file mode 100644 index 0000000..8ef5491 Binary files /dev/null and b/res/bullets/smallpink.png differ diff --git a/res/bullets/smallred.png b/res/bullets/smallred.png new file mode 100644 index 0000000..bc8056e Binary files /dev/null and b/res/bullets/smallred.png differ diff --git a/res/bullets/smallyellow.png b/res/bullets/smallyellow.png new file mode 100644 index 0000000..853fd67 Binary files /dev/null and b/res/bullets/smallyellow.png differ diff --git a/res/chrome/bossbar.png b/res/chrome/bossbar.png new file mode 100644 index 0000000..1950af4 Binary files /dev/null and b/res/chrome/bossbar.png differ diff --git a/res/chrome/easy.png b/res/chrome/easy.png new file mode 100644 index 0000000..614a496 Binary files /dev/null and b/res/chrome/easy.png differ diff --git a/res/chrome/enemy.png b/res/chrome/enemy.png new file mode 100644 index 0000000..25af6ee Binary files /dev/null and b/res/chrome/enemy.png differ diff --git a/res/chrome/font.png b/res/chrome/font.png new file mode 100644 index 0000000..298e467 Binary files /dev/null and b/res/chrome/font.png differ diff --git a/res/chrome/frame1.png b/res/chrome/frame1.png new file mode 100644 index 0000000..7860cc8 Binary files /dev/null and b/res/chrome/frame1.png differ diff --git a/res/chrome/frame2.png b/res/chrome/frame2.png new file mode 100644 index 0000000..1f03c27 Binary files /dev/null and b/res/chrome/frame2.png differ diff --git a/res/chrome/frame3.png b/res/chrome/frame3.png new file mode 100644 index 0000000..72aa067 Binary files /dev/null and b/res/chrome/frame3.png differ diff --git a/res/chrome/frame4.png b/res/chrome/frame4.png new file mode 100644 index 0000000..7608346 Binary files /dev/null and b/res/chrome/frame4.png differ diff --git a/res/chrome/frame5.png b/res/chrome/frame5.png new file mode 100644 index 0000000..4ff731d Binary files /dev/null and b/res/chrome/frame5.png differ diff --git a/res/chrome/girl.png b/res/chrome/girl.png new file mode 100644 index 0000000..0088df4 Binary files /dev/null and b/res/chrome/girl.png differ diff --git a/res/chrome/hard.png b/res/chrome/hard.png new file mode 100644 index 0000000..2552209 Binary files /dev/null and b/res/chrome/hard.png differ diff --git a/res/chrome/player.png b/res/chrome/player.png new file mode 100644 index 0000000..f0145fd Binary files /dev/null and b/res/chrome/player.png differ diff --git a/res/chrome/stage.png b/res/chrome/stage.png new file mode 100644 index 0000000..39822fe Binary files /dev/null and b/res/chrome/stage.png differ diff --git a/res/enemies/cake.png b/res/enemies/cake.png new file mode 100644 index 0000000..1b0c8b5 Binary files /dev/null and b/res/enemies/cake.png differ diff --git a/res/enemies/gumdropblue.png b/res/enemies/gumdropblue.png new file mode 100644 index 0000000..4f87d84 Binary files /dev/null and b/res/enemies/gumdropblue.png differ diff --git a/res/enemies/gumdropgreen.png b/res/enemies/gumdropgreen.png new file mode 100644 index 0000000..5370b5a Binary files /dev/null and b/res/enemies/gumdropgreen.png differ diff --git a/res/enemies/gumdropred.png b/res/enemies/gumdropred.png new file mode 100644 index 0000000..a8dd598 Binary files /dev/null and b/res/enemies/gumdropred.png differ diff --git a/res/enemies/gumdropyellow.png b/res/enemies/gumdropyellow.png new file mode 100644 index 0000000..d7eb0e5 Binary files /dev/null and b/res/enemies/gumdropyellow.png differ diff --git a/res/enemies/parfait.png b/res/enemies/parfait.png new file mode 100644 index 0000000..d57f7ff Binary files /dev/null and b/res/enemies/parfait.png differ diff --git a/res/enemies/peppermintblue.png b/res/enemies/peppermintblue.png new file mode 100644 index 0000000..270078b Binary files /dev/null and b/res/enemies/peppermintblue.png differ diff --git a/res/enemies/peppermintgreen.png b/res/enemies/peppermintgreen.png new file mode 100644 index 0000000..5592839 Binary files /dev/null and b/res/enemies/peppermintgreen.png differ diff --git a/res/enemies/peppermintred.png b/res/enemies/peppermintred.png new file mode 100644 index 0000000..c87ec4d Binary files /dev/null and b/res/enemies/peppermintred.png differ diff --git a/res/enemies/peppermintyellow.png b/res/enemies/peppermintyellow.png new file mode 100644 index 0000000..336fc14 Binary files /dev/null and b/res/enemies/peppermintyellow.png differ diff --git a/res/enemies/roll.png b/res/enemies/roll.png new file mode 100644 index 0000000..96d7b48 Binary files /dev/null and b/res/enemies/roll.png differ diff --git a/res/enemies/spider.png b/res/enemies/spider.png new file mode 100644 index 0000000..e5c9b4a Binary files /dev/null and b/res/enemies/spider.png differ diff --git a/res/enemies/waffle.png b/res/enemies/waffle.png new file mode 100644 index 0000000..35c554f Binary files /dev/null and b/res/enemies/waffle.png differ diff --git a/res/enemies/yin1.png b/res/enemies/yin1.png new file mode 100644 index 0000000..d7551ef Binary files /dev/null and b/res/enemies/yin1.png differ diff --git a/res/explosions/player.png b/res/explosions/player.png new file mode 100644 index 0000000..f869732 Binary files /dev/null and b/res/explosions/player.png differ diff --git a/res/full.png b/res/full.png new file mode 100644 index 0000000..97bd9fb Binary files /dev/null and b/res/full.png differ diff --git a/res/half.png b/res/half.png new file mode 100644 index 0000000..f6f1f68 Binary files /dev/null and b/res/half.png differ diff --git a/res/least.png b/res/least.png new file mode 100644 index 0000000..8d46390 Binary files /dev/null and b/res/least.png differ diff --git a/res/player/bullet.png b/res/player/bullet.png new file mode 100644 index 0000000..28d2e18 Binary files /dev/null and b/res/player/bullet.png differ diff --git a/res/player/player.png b/res/player/player.png new file mode 100644 index 0000000..854e831 Binary files /dev/null and b/res/player/player.png differ diff --git a/res/player/reimu.png b/res/player/reimu.png new file mode 100644 index 0000000..5135ba3 Binary files /dev/null and b/res/player/reimu.png differ diff --git a/res/resources.res b/res/resources.res new file mode 100644 index 0000000..d59b801 --- /dev/null +++ b/res/resources.res @@ -0,0 +1,94 @@ +IMAGE font "chrome/font.png" BEST NONE + +IMAGE full "full.png" FAST +IMAGE half "half.png" FAST +IMAGE least "least.png" FAST + +IMAGE ground1 "bg/ground1.png" FAST +IMAGE ground2 "bg/ground2.png" FAST +IMAGE ground3 "bg/ground3.png" FAST +IMAGE ground4 "bg/ground4.png" FAST +IMAGE ground5 "bg/ground5.png" FAST +IMAGE ground6 "bg/ground6.png" FAST +IMAGE ground7 "bg/ground7.png" FAST +IMAGE ground8 "bg/ground8.png" FAST + +IMAGE frame1 "chrome/frame1.png" FAST +IMAGE frame2 "chrome/frame2.png" FAST +IMAGE frame3 "chrome/frame3.png" FAST +IMAGE frame4 "chrome/frame4.png" FAST +IMAGE frame5 "chrome/frame5.png" FAST +IMAGE bossBar "chrome/bossbar.png" FAST +IMAGE chromePlayer "chrome/player.png" FAST +IMAGE chromeStage "chrome/stage.png" FAST +IMAGE chromeHard "chrome/hard.png" FAST +IMAGE chromeEasy "chrome/easy.png" FAST +IMAGE chromeEnemy "chrome/enemy.png" FAST + +SPRITE imgPlayer "player/player.png" 3 4 FAST 20 +SPRITE imgBullet "player/bullet.png" 2 2 FAST + +SPRITE imgYin1 "enemies/yin1.png" 1 1 FAST 5 + +SPRITE imgGumdropRed "enemies/gumdropred.png" 2 2 FAST 20 +SPRITE imgGumdropGreen "enemies/gumdropgreen.png" 2 2 FAST 20 +SPRITE imgGumdropBlue "enemies/gumdropblue.png" 2 2 FAST 20 +SPRITE imgGumdropYellow "enemies/gumdropyellow.png" 2 2 FAST 20 + +SPRITE imgPeppermintRed "enemies/peppermintred.png" 2 2 FAST 30 +SPRITE imgPeppermintGreen "enemies/peppermintgreen.png" 2 2 FAST 10 +SPRITE imgPeppermintBlue "enemies/peppermintblue.png" 2 2 FAST 10 +SPRITE imgPeppermintYellow "enemies/peppermintyellow.png" 2 2 FAST 10 + +SPRITE imgSpider "enemies/spider.png" 2 2 FAST 20 + +IMAGE cake "enemies/cake.png" FAST +IMAGE parfait "enemies/parfait.png" FAST +IMAGE waffle "enemies/waffle.png" FAST +IMAGE roll "enemies/roll.png" FAST + +SPRITE imgSmallRedBullet "bullets/smallred.png" 1 1 FAST 5 +SPRITE imgSmallBlueBullet "bullets/smallblue.png" 1 1 FAST 5 +SPRITE imgSmallPinkBullet "bullets/smallpink.png" 1 1 FAST 5 + +SPRITE imgBigRedBullet "bullets/bigred.png" 2 2 FAST 5 +SPRITE imgBigBlueBullet "bullets/bigblue.png" 2 2 FAST 5 +SPRITE imgBigPinkBullet "bullets/bigpink.png" 2 2 FAST 5 + +SPRITE imgExplosionPlayer "explosions/player.png" 3 3 FAST 5 + +IMAGE girl "chrome/girl.png" FAST + + +IMAGE startTop "start/top.png" FAST +IMAGE startGradient "start/gradient.png" FAST +IMAGE startBottom "start/bottom.png" FAST +IMAGE startLogo "start/logo.png" FAST +IMAGE startLogoV "start/logov.png" FAST +IMAGE startAbout "start/about.png" FAST +IMAGE startAbout2 "start/about2.png" FAST + +SPRITE startBoddy1 "start/boddy.png" 7 4 FAST 5 +SPRITE startBoddy2 "start/boddy2.png" 7 4 FAST 5 + +XGM bgmStart "bgm/start.vgm" 0 +XGM bgmStage1 "bgm/stage1.vgm" 0 +XGM bgmStage2 "bgm/stage2.vgm" 0 + +WAV sfxMenuSelect "sfx/menuselect.wav" 5 +WAV sfxMenuChoose "sfx/menuchoose.wav" 5 +WAV sfxStartGame "sfx/startgame.wav" 5 +WAV sfxZoneOver "sfx/zoneover.wav" 5 + +WAV sfxPlayerHit "sfx/playerhit.wav" 5 +WAV sfxPlayerShot "sfx/playershot.wav" 5 + +WAV sfxBullet1 "sfx/bullet1.wav" 5 + +WAV sfxExplosion1 "sfx/explosion1.wav" 5 +WAV sfxExplosion2 "sfx/explosion2.wav" 5 +WAV sfxExplosion3 "sfx/explosion3.wav" 5 + +WAV sfxGameOver "sfx/gameover.wav" 5 +WAV sfxBeatGame "sfx/beatgame.wav" 5 + diff --git a/res/sfx/beatgame.wav b/res/sfx/beatgame.wav new file mode 100644 index 0000000..9c56545 Binary files /dev/null and b/res/sfx/beatgame.wav differ diff --git a/res/sfx/bullet1.wav b/res/sfx/bullet1.wav new file mode 100644 index 0000000..29759e6 Binary files /dev/null and b/res/sfx/bullet1.wav differ diff --git a/res/sfx/bullet2.wav b/res/sfx/bullet2.wav new file mode 100644 index 0000000..3566a3e Binary files /dev/null and b/res/sfx/bullet2.wav differ diff --git a/res/sfx/bullet3.wav b/res/sfx/bullet3.wav new file mode 100644 index 0000000..c63ee48 Binary files /dev/null and b/res/sfx/bullet3.wav differ diff --git a/res/sfx/bullet4.wav b/res/sfx/bullet4.wav new file mode 100644 index 0000000..a91d923 Binary files /dev/null and b/res/sfx/bullet4.wav differ diff --git a/res/sfx/explosion1.wav b/res/sfx/explosion1.wav new file mode 100644 index 0000000..7dcad45 Binary files /dev/null and b/res/sfx/explosion1.wav differ diff --git a/res/sfx/explosion2.wav b/res/sfx/explosion2.wav new file mode 100644 index 0000000..bd529bf Binary files /dev/null and b/res/sfx/explosion2.wav differ diff --git a/res/sfx/explosion3.wav b/res/sfx/explosion3.wav new file mode 100644 index 0000000..5176281 Binary files /dev/null and b/res/sfx/explosion3.wav differ diff --git a/res/sfx/gameover.wav b/res/sfx/gameover.wav new file mode 100644 index 0000000..2100aa3 Binary files /dev/null and b/res/sfx/gameover.wav differ diff --git a/res/sfx/menuchoose.wav b/res/sfx/menuchoose.wav new file mode 100644 index 0000000..e8a5def Binary files /dev/null and b/res/sfx/menuchoose.wav differ diff --git a/res/sfx/menuselect.wav b/res/sfx/menuselect.wav new file mode 100644 index 0000000..6d2e5f1 Binary files /dev/null and b/res/sfx/menuselect.wav differ diff --git a/res/sfx/playerhit.wav b/res/sfx/playerhit.wav new file mode 100644 index 0000000..b0a3f2f Binary files /dev/null and b/res/sfx/playerhit.wav differ diff --git a/res/sfx/playershot.wav b/res/sfx/playershot.wav new file mode 100644 index 0000000..e518440 Binary files /dev/null and b/res/sfx/playershot.wav differ diff --git a/res/sfx/startgame.wav b/res/sfx/startgame.wav new file mode 100644 index 0000000..5cbaa79 Binary files /dev/null and b/res/sfx/startgame.wav differ diff --git a/res/sfx/zoneover.wav b/res/sfx/zoneover.wav new file mode 100644 index 0000000..a7bd1f3 Binary files /dev/null and b/res/sfx/zoneover.wav differ diff --git a/res/start/about.png b/res/start/about.png new file mode 100644 index 0000000..21bdaac Binary files /dev/null and b/res/start/about.png differ diff --git a/res/start/about2.png b/res/start/about2.png new file mode 100644 index 0000000..c75faf1 Binary files /dev/null and b/res/start/about2.png differ diff --git a/res/start/boddy.png b/res/start/boddy.png new file mode 100644 index 0000000..5e1a058 Binary files /dev/null and b/res/start/boddy.png differ diff --git a/res/start/boddy2.png b/res/start/boddy2.png new file mode 100644 index 0000000..fe441b5 Binary files /dev/null and b/res/start/boddy2.png differ diff --git a/res/start/bottom.png b/res/start/bottom.png new file mode 100644 index 0000000..3c82f27 Binary files /dev/null and b/res/start/bottom.png differ diff --git a/res/start/gradient.png b/res/start/gradient.png new file mode 100644 index 0000000..0a8ab1a Binary files /dev/null and b/res/start/gradient.png differ diff --git a/res/start/logo.png b/res/start/logo.png new file mode 100644 index 0000000..7cf86a4 Binary files /dev/null and b/res/start/logo.png differ diff --git a/res/start/logov.png b/res/start/logov.png new file mode 100644 index 0000000..6c1ad9d Binary files /dev/null and b/res/start/logov.png differ diff --git a/res/start/top.png b/res/start/top.png new file mode 100644 index 0000000..4ea9366 Binary files /dev/null and b/res/start/top.png differ diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..b27cb9e --- /dev/null +++ b/run.sh @@ -0,0 +1 @@ +./blastem/blastem out.bin \ No newline at end of file diff --git a/src/background.c b/src/background.c new file mode 100644 index 0000000..59f6ab6 --- /dev/null +++ b/src/background.c @@ -0,0 +1,48 @@ +#include +#include + +#include "main.h" +#include "background.h" + + +void resetBackground(){ + for(s8 y = 0; y < BG_TILES_HEIGHT; y++){ + for(s8 x = 0; x < BG_TILES_WIDTH; x++){ + if(x % 8 == 0 && y % 8 == 0){ + if(currentZone < 5) VDP_drawImageEx(BG_B, &ground1, TILE_ATTR_FULL(PAL1, 0, 0, 0, 16), x, y, 0, DMA_QUEUE); + else if(currentZone == 5) VDP_drawImageEx(BG_B, &ground2, TILE_ATTR_FULL(PAL1, 0, 0, 0, 16), x, y, 0, DMA_QUEUE); + else if(currentZone > 5 && currentZone < 10) VDP_drawImageEx(BG_B, &ground3, TILE_ATTR_FULL(PAL1, 0, 0, 0, 16), x, y, 0, DMA_QUEUE); + else if(currentZone == 10) VDP_drawImageEx(BG_B, &ground4, TILE_ATTR_FULL(PAL1, 0, 0, 0, 16), x, y, 0, DMA_QUEUE); + else if(currentZone > 10 && currentZone < 15) VDP_drawImageEx(BG_B, &ground5, TILE_ATTR_FULL(PAL1, 0, 0, 0, 16), x, y, 0, DMA_QUEUE); + else if(currentZone == 15) VDP_drawImageEx(BG_B, &ground6, TILE_ATTR_FULL(PAL1, 0, 0, 0, 16), x, y, 0, DMA_QUEUE); + else if(currentZone > 15 && currentZone < 20) VDP_drawImageEx(BG_B, &ground7, TILE_ATTR_FULL(PAL1, 0, 0, 0, 16), x, y, 0, DMA_QUEUE); + else if(currentZone == 20) VDP_drawImageEx(BG_B, &ground8, TILE_ATTR_FULL(PAL1, 0, 0, 0, 16), x, y, 0, DMA_QUEUE); + } + VDP_setTileMapXY(BG_A, TILE_ATTR_FULL(PAL1, 0, 0, 0, 11), x, y); + } + } +} + +// stage + +void loadBackgroundStage(){ + for(s8 i = 0; i < BG_SCROLL_WIDTH; i++){ + backgroundSpeeds[i] = i < 8 ? BG_SCROLL_WIDTH - i - 2 : i - 1; + backgroundSpeeds[i]--; + } + VDP_setScrollingMode(HSCROLL_PLANE, VSCROLL_2TILE); + resetBackground(); +} + +void updateBackgroundStage(){ + for(s8 i = 0; i < BG_SCROLL_WIDTH; i++) backgroundScrolls[i] -= fix16ToRoundedInt(FIX16(backgroundSpeeds[i] / 2)) - 1; + VDP_setVerticalScrollTile(BG_B, 0, backgroundScrolls, BG_SCROLL_WIDTH, DMA_QUEUE); +} + + +// loop + +void updateBackground(){ + if(gameStarting) loadBackgroundStage(); + else updateBackgroundStage(); +} diff --git a/src/background.h b/src/background.h new file mode 100644 index 0000000..d7b02a7 --- /dev/null +++ b/src/background.h @@ -0,0 +1,15 @@ +#define BG_TILES_WIDTH 32 +#define BG_TILES_HEIGHT 32 +#define BG_SCROLL_WIDTH 16 + +s16 backgroundY; +s16 backgroundScrolls[BG_SCROLL_WIDTH], + backgroundSpeeds[BG_SCROLL_WIDTH]; + +void loadBackgroundStage(), + loadBackgroundBoss(), + loadBackgroundTiles(), + resetBackground(), + updateBackgroundStage(), + updateBackgroundBoss(), + updateBackground(); \ No newline at end of file diff --git a/src/boss.c b/src/boss.c new file mode 100644 index 0000000..8d21399 --- /dev/null +++ b/src/boss.c @@ -0,0 +1,565 @@ +#include +#include + +#include "main.h" +#include "enemies.h" +#include "explosion.h" +#include "player.h" +#include "chrome.h" +#include "boss.h" + + +// shooting + +void bossPatternOne(){ + if(bossClock % 10 == 0 && bossClock % 60 <= 50){ + if(bossClock % 60 == 0) bossInt1 = random() % 1024; + struct bulletSpawner bSpawn = { + .x = FIX16(bossClock % 120 < 60 ? 208 : 48), + .y = BOSS_B_Y, + .type = 1, + .angle = bossInt1, + .speed = FIX16(5) + }; + for(s8 b = 0; b < 6; b++){ + if(bSpawn.angle % 1024 >= 0 && bSpawn.angle % 1024 <= 512) spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += 170; + } + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + bossInt1 += bossClock % 120 < 60 ? 32 : -32; + if(bossClock % 30 == 0) spawnExplosion(fix16ToInt(bSpawn.x), fix16ToInt(bSpawn.y), FALSE); + if(bossClock % 20 == 10){ + bSpawn.velocityX = honeEnemyBullet(bSpawn.x, bSpawn.y, 4, 96, TRUE); + bSpawn.velocityY = honeEnemyBullet(bSpawn.x, bSpawn.y, 4, 16, FALSE); + bSpawn.type = 4; + spawnEnemyBullet(bSpawn, eUpdate); + XGM_startPlayPCM(SFX_BULLET_1, 1, SOUND_PCM_CH3); + } + } +} + +void bossPatternTwo(){ + if(bossClock % 60 == 0){ + bossInt1 = 240 + random() % 32; + bossInt2 = bossClock % 120 == 0 ? 48 : -48; + bossInt3 = bossInt2 / 2; + } + if(bossClock % 8 == 0){ + struct bulletSpawner bSpawn = { + .x = FIX16(bossClock % 120 < 60 ? 208 : 48), + .y = BOSS_B_Y, + .type = 4, + .angle = bossInt1 - bossInt2, + .speed = FIX16(5.5) + }; + spawnExplosion(fix16ToInt(bSpawn.x), fix16ToInt(bSpawn.y), FALSE); + for(s8 b = 0; b < 3; b++){ + spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.speed = fix16Add(bSpawn.speed, FIX16(1.5)); + bSpawn.angle += bossInt3; + } + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + bossInt1 += bossInt2; + } +} + +void bossPatternThree(){ + if(bossClock % 60 < 18 && bossClock % 6 == 0){ + if(bossClock % 60 == 0){ + bossInt1 = 3; + bossInt2 = bossClock % 120 == 0 ? 68 : -68; + bossInt3 = bossClock % 120 == 0 ? 224 : 288; + bossFix1 = FIX16(bossClock % 120 < 60 ? 208 : 48); + } + struct bulletSpawner bSpawn = { + .x = bossFix1, + .y = BOSS_B_Y, + .type = 3, + .angle = bossInt3, + .speed = FIX16(bossInt1) + }; + if(bossClock % 60 == 0) spawnExplosion(fix16ToInt(bSpawn.x), fix16ToInt(bSpawn.y), FALSE); + for(s8 b = 0; b < 4; b++){ + spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += bossInt2; + } + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + bossInt1++; + } + else if(bossClock % 60 >= 30 && bossClock % 5 == 0){ + if(bossClock % 60 == 30){ + bossFix1 = FIX16(32 + random() % 192); + spawnExplosion(fix16ToInt(bossFix1), fix16ToInt(BOSS_B_Y), FALSE); + } + struct bulletSpawner bSpawn = { + .x = bossFix1, + .y = BOSS_B_Y, + .type = 2 + }; + bSpawn.velocityX = honeEnemyBullet(bSpawn.x, bSpawn.y, 4, 96, TRUE); + bSpawn.velocityY = honeEnemyBullet(bSpawn.x, bSpawn.y, 4, 16, FALSE); + spawnEnemyBullet(bSpawn, eUpdate); + XGM_startPlayPCM(SFX_BULLET_1, 1, SOUND_PCM_CH3); + } +} + +void bossPatternFour(){ + if(bossClock % 60 <= 10 && bossClock % 5 == 0){ + if(bossClock % 60 == 0) bossInt1 = 4; + struct bulletSpawner bSpawn = { + .x = BOSS_B_X, + .y = BOSS_B_Y, + .speed = FIX16(bossInt1) + }; + bSpawn.angle = bossClock % 60 == 5 ? 32 : 0; + bSpawn.type = bossClock % 60 == 0 ? 3 : 4; + for(s8 b = 0; b < (bossClock % 60 == 5 ? 5 : 6); b++){ + bSpawn.angle += 64; + if(b > 0) spawnEnemyBullet(bSpawn, eUpdate); + } + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + bossInt1++; + } else if(bossClock % 60 >= 30 && bossClock % 5 == 0){ + struct bulletSpawner bSpawn = { + .x = FIX16(bossClock % 10 == 0 ? 208 : 48), + .y = BOSS_B_Y, + .type = 1 + }; + bSpawn.velocityX = honeEnemyBullet(bSpawn.x, bSpawn.y, 4, 96, TRUE); + bSpawn.velocityY = honeEnemyBullet(bSpawn.x, bSpawn.y, 4, 16, FALSE); + spawnEnemyBullet(bSpawn, eUpdate); + XGM_startPlayPCM(SFX_BULLET_1, 1, SOUND_PCM_CH3); + spawnExplosion(fix16ToInt(bSpawn.x), fix16ToInt(bSpawn.y), FALSE); + } +} + +void bossPatternFive(){ + if(bossClock % 60 == 0){ + struct bulletSpawner bSpawn = { + .x = BOSS_B_X, + .y = BOSS_B_Y, + .angle = random() % 1024, + .speed = FIX16(4), + .type = 4 + }; + for(s8 b = 0; b < 16; b++){ + if(bSpawn.angle % 1024 > 0 && bSpawn.angle % 1024 < 512){ + spawnEnemyBullet(bSpawn, eUpdate); + } + bSpawn.angle += 64; + } + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + } else if(bossClock % 60 >= 30 && bossClock % 60 < 45 && bossClock % 5 == 0){ + struct bulletSpawner bSpawn = { + .x = BOSS_B_X, + .y = BOSS_B_Y, + .type = bossClock % 60 >= 38 ? 3 : 4 + }; + bSpawn.velocityX = honeEnemyBullet(bSpawn.x, bSpawn.y, 5, 128, TRUE); + bSpawn.velocityY = honeEnemyBullet(bSpawn.x, bSpawn.y, 5, 16, FALSE); + spawnEnemyBullet(bSpawn, eUpdate); + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + } + if(bossClock % 60 >= 30 && bossClock % 5 == 3){ + struct bulletSpawner bSpawn = { + .x = FIX16(bossClock % 30 < 15 ? 208 : 48), + .y = BOSS_B_Y, + .type = bossClock % 15 > 6 ? 1 : 2 + }; + if(bossClock % 15 == 0){ + bossFix1 = bSpawn.x; + bossFix2 = bSpawn.y; + } + spawnExplosion(fix16ToInt(bSpawn.x), fix16ToInt(bSpawn.y), FALSE); + bSpawn.velocityX = honeEnemyBullet(bossFix1, bossFix2, 4, 32, TRUE); + bSpawn.velocityY = honeEnemyBullet(bossFix1, bossFix2, 4, 16, FALSE); + spawnEnemyBullet(bSpawn, eUpdate); + XGM_startPlayPCM(SFX_BULLET_1, 1, SOUND_PCM_CH3); + } +} + +void bossPatternSix(){ + if(bossClock % 5 == 0 && bossClock % 120 < 110){ + if(bossClock % 120 == 0){ + bossInt1 = 0 + random() % 32; + bossInt2 = 512 - random() % 32; + bossInt3 = 1; + } + struct bulletSpawner bSpawn = { + .x = FIX16(48), + .y = BOSS_B_Y, + .angle = bossInt1, + .speed = FIX16(7), + .type = bossClock % 240 < 120 ? 4 : 2 + }; + if(bossClock % 10 == 0) spawnExplosion(fix16ToInt(bSpawn.x), fix16ToInt(bSpawn.y), FALSE); + for(s8 b = 0; b < 2; b++){ + if(bSpawn.angle % 1024 < 512) spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += 512; + } + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + bSpawn.x = FIX16(208); + bSpawn.angle = bossInt2; + if(bossClock % 10 == 5) spawnExplosion(fix16ToInt(bSpawn.x), fix16ToInt(bSpawn.y), FALSE); + for(s8 b = 0; b < 2; b++){ + if((bSpawn.angle < 0 && abs(bSpawn.angle) % 1024 > 512) || (bSpawn.angle >= 0 && bSpawn.angle < 512)) spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += 512; + } + bossInt1 += 5 * bossInt3; + bossInt2 -= 10 * bossInt3; + bossInt3++; + } +} + +void bossPatternSeven(){ + if(bossClock % 120 < 100){ + if(bossClock % 120 == 0) bossInt1 = 0; + if(bossClock % 15 == 0){ + struct bulletSpawner bSpawn = { + .x = BOSS_B_X, + .y = BOSS_B_Y, + .angle = 0 + bossInt1, + .speed = FIX16(8), + .type = 2 + }; + for(s8 b = 0; b < 16; b++){ + if(bSpawn.angle % 1024 > 32 && bSpawn.angle % 1024 < 480) spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += 64; + } + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + bossInt1 += 24; + } + if(bossClock % 10 == 5){ + struct bulletSpawner bSpawn = { + .x = BOSS_B_X, + .y = BOSS_B_Y, + .type = 3 + }; + bSpawn.velocityX = honeEnemyBullet(bSpawn.x, bSpawn.y, 5, 128, TRUE); + bSpawn.velocityY = honeEnemyBullet(bSpawn.x, bSpawn.y, 5, 16, FALSE); + spawnEnemyBullet(bSpawn, eUpdate); + XGM_startPlayPCM(SFX_BULLET_1, 1, SOUND_PCM_CH3); + } + } +} + +void bossPatternEight(){ + if(bossClock % 30 == 0){ + struct bulletSpawner bSpawn = { + .x = FIX16(bossClock % 60 == 0 ? 208 : 48), + .y = BOSS_B_Y, + .angle = random() % 1024, + .speed = FIX16(8), + .type = 1 + }; + spawnExplosion(fix16ToInt(bSpawn.x), fix16ToInt(bSpawn.y), FALSE); + for(s8 b = 0; b < 8; b++){ + if(bSpawn.angle % 1024 > 0 && bSpawn.angle % 1024 < 512){ + spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += 48; + spawnEnemyBullet(bSpawn, eUpdate); + } else bSpawn.angle += 48; + bSpawn.angle += 80; + } + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + } else if(bossClock % 30 == 15){ + struct bulletSpawner bSpawn = { + .x = FIX16(bossClock % 60 == 15 ? 192 : 64), + .y = BOSS_B_Y, + .angle = random() % 1024, + .speed = FIX16(6), + .type = 4 + }; + spawnExplosion(fix16ToInt(bSpawn.x), fix16ToInt(bSpawn.y), FALSE); + for(s8 b = 0; b < 16; b++){ + if(bSpawn.angle % 1024 > 0 && bSpawn.angle % 1024 < 512) spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += 64; + } + XGM_startPlayPCM(SFX_BULLET_1, 1, SOUND_PCM_CH3); + } +} + +void bossPatternNine(){ + if(bossClock % 60 < 40 && bossClock % 6 == 0){ + struct bulletSpawner bSpawn = { + .x = FIX16(64 + random() % 128), + .y = BOSS_B_Y, + .type = 3 + }; + spawnExplosion(fix16ToInt(bSpawn.x), fix16ToInt(bSpawn.y), FALSE); + for(s8 b = 0; b < 4; b++){ + bSpawn.type = b % 2 == 0 ? 3 : 4; + bSpawn.velocityX = honeEnemyBullet(bSpawn.x, bSpawn.y, 5, 192, TRUE); + bSpawn.velocityY = honeEnemyBullet(bSpawn.x, bSpawn.y, 5, 8, FALSE); + spawnEnemyBullet(bSpawn, eUpdate); + } + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + } +} + +void bossPatternTen(){ + if(bossClock % 15 == 0){ + struct bulletSpawner bSpawn = { + .x = FIX16(64 + random() % 128), + .y = BOSS_B_Y, + .type = bossClock % 30 == 0 ? 4 : 3, + .angle = random() % 1024, + .speed = FIX16(bossClock % 30 == 0 ? 7 : 5) + }; + spawnExplosion(fix16ToInt(bSpawn.x), fix16ToInt(bSpawn.y), FALSE); + for(s8 b = 0; b < 16; b++){ + if(bSpawn.angle % 1024 > 0 && bSpawn.angle % 1024 < 512) spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += 64; + } + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + } + else if(bossClock % 60 > 15 && bossClock % 60 < 30 && bossClock % 2 == 0){ + struct bulletSpawner bSpawn = { + .x = BOSS_B_X, + .y = BOSS_B_Y, + .type = bossClock % 4 == 0 ? 2 : 1 + }; + bSpawn.velocityX = honeEnemyBullet(bSpawn.x, bSpawn.y, 6, 96, TRUE); + bSpawn.velocityY = honeEnemyBullet(bSpawn.x, bSpawn.y, 6, 8, FALSE); + spawnEnemyBullet(bSpawn, eUpdate); + XGM_startPlayPCM(SFX_BULLET_1, 1, SOUND_PCM_CH3); + } +} + +void bossPatternEleven(){ + if(bossClock % 60 < 30 && bossClock % 4 == 0){ + struct bulletSpawner bSpawn = { + .x = FIX16(bossClock % 120 < 60 ? 208 : 48), + .y = BOSS_B_Y, + .type = 3 + }; + if(bossClock % 60 == 0) bossInt1 = 4; + if(bossClock % 60 == 0 || bossClock % 60 == 12) spawnExplosion(fix16ToInt(bSpawn.x), fix16ToInt(bSpawn.y), FALSE); + bSpawn.velocityX = honeEnemyBullet(bSpawn.x, bSpawn.y, bossInt1, 64, TRUE); + bSpawn.velocityY = honeEnemyBullet(bSpawn.x, bSpawn.y, bossInt1, 8, FALSE); + spawnEnemyBullet(bSpawn, eUpdate); + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + bossInt1++; + } + if(bossClock % 15 == 5){ + struct bulletSpawner bSpawn = { + .x = BOSS_B_X, + .y = BOSS_B_Y, + .type = 2, + .angle = bossClock % 30 == 5 ? 32 : 0, + .speed = FIX16(6) + }; + for(s8 b = 0; b < 16; b++){ + if(bSpawn.angle % 1024 > 0 && bSpawn.angle % 1024 < 512) spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += 64; + } + XGM_startPlayPCM(SFX_BULLET_1, 1, SOUND_PCM_CH3); + } +} + +void bossPatternTwelve(){ + if(bossClock % 60 < 35 && bossClock % 6 == 0){ + if(bossClock % 60 == 0){ + bossInt1 = 320; + bossInt2 = 192; + } + struct bulletSpawner bSpawn = { + .x = FIX16(bossClock % 12 == 0 ? 208 : 48), + .y = BOSS_B_Y, + .speed = FIX16(7), + .angle = bossClock % 12 == 0 ? bossInt2 : bossInt1, + .type = 4 + }; + for(s8 b = 0; b < 4; b++){ + spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += bossClock % 12 == 0 ? 64 : -64; + } + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + bossInt1 -= 8; + bossInt2 += 8; + } + if(bossClock % 3 == 1 && bossClock % 30 < 15){ + struct bulletSpawner bSpawn = { + .x = BOSS_B_X, + .y = BOSS_B_Y, + .type = 1 + }; + if(bossClock % 30 == 1){ + bossFix1 = honeEnemyBullet(bSpawn.x, bSpawn.y, 7, 0, TRUE); + bossFix2 = honeEnemyBullet(bSpawn.x, bSpawn.y, 7, 0, FALSE); + } + bSpawn.velocityX = bossFix1; + bSpawn.velocityY = bossFix2; + spawnEnemyBullet(bSpawn, eUpdate); + XGM_startPlayPCM(SFX_BULLET_1, 1, SOUND_PCM_CH3); + } +} + +void bossPatternThirteen(){ + if(bossClock % 5 == 0){ + if(bossClock == 0){ + bossInt1 = 0; + bossInt2 = 1024; + } + struct bulletSpawner bSpawn = { + .x = FIX16(bossClock % 10 == 0 ? 208 : 48), + .y = BOSS_B_Y, + .type = 3, + .angle = bossClock % 10 == 0 ? bossInt2 : bossInt1, + .speed = FIX16(6) + }; + spawnExplosion(fix16ToInt(bSpawn.x), fix16ToInt(bSpawn.y), FALSE); + for(s8 b = 0; b < 5; b++){ + if(bSpawn.angle % 1024 > 0 && bSpawn.angle % 1024 < 512) spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += 205; + } + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + bossInt1 += 19; + bossInt2 -= 15; + if(bossInt1 >= 1024) bossInt1 = 0; + if(bossInt2 <= 0) bossInt2 = 1024; + } + if(bossClock % 60 == 35 || bossClock % 60 == 36){ + struct bulletSpawner bSpawn = { + .x = BOSS_B_X, + .y = BOSS_B_Y, + .type = bossClock % 60 == 35 ? 2 : 1, + .angle = bossClock % 60 == 35 ? 0 : 32, + .speed = FIX16(bossClock % 60 == 35 ? 5 : 8) + }; + for(s8 b = 0; b < 8; b++){ + if(b > 0 && (bossClock % 60 == 35 || (bossClock % 60 == 36 && b < 7))) spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += 64; + } + XGM_startPlayPCM(SFX_BULLET_1, 1, SOUND_PCM_CH3); + } +} + +void hitBossPattern(u8 i){ + if(!hitBossPatterns[i]){ + hitBossPatterns[i] = TRUE; + bossClock = BOSS_SWITCH_TIME; + killBullets = TRUE; + } +} + +void shootBoss(){ + switch(bossType){ + case 1: + if(bossHealth < 50){ + hitBossPattern(0); + if(bossClock >= 0) bossPatternTwo(); + } else if(bossClock >= 0) bossPatternOne(); + break; + case 2: + if(bossHealth < 50){ + hitBossPattern(2); + if(bossClock >= 0) bossPatternFour(); + } else if(bossHealth < 100){ + hitBossPattern(1); + if(bossClock >= 0) bossPatternFive(); + } else if(bossClock >= 0) bossPatternThree(); + break; + case 3: + if(bossHealth < 50){ + hitBossPattern(4); + if(bossClock >= 0) bossPatternEight(); + } else if(bossHealth < 100){ + hitBossPattern(3); + if(bossClock >= 0) bossPatternSeven(); + } else if(bossClock >= 0) bossPatternSix(); + break; + case 4: + if(bossHealth < 50){ + hitBossPattern(8); + if(bossClock >= 0) bossPatternThirteen(); + } else if(bossHealth < 100){ + hitBossPattern(7); + if(bossClock >= 0) bossPatternTwelve(); + } else if(bossHealth < 150){ + hitBossPattern(6); + if(bossClock >= 0) bossPatternEleven(); + } else if(bossHealth < 200){ + hitBossPattern(5); + if(bossClock >= 0) bossPatternTen(); + } else if(bossClock >= 0) bossPatternNine(); + break; + } +} + + +// collision + +void hitBoss(){ + bossHealth--; + // bossHealth -= 10; + if(bossHealth <= 0) finishBoss(); +} + +void collideBoss(){ + if(bombing && bossClock % 10 == 0) bossHealth--; + for(s16 j = 0; j < PLAYER_BULLET_LIMIT; j++) if(playerBullets[j].active) { + bossCollisionDistance = getApproximatedDistance( + fix32Sub(fix16ToFix32(playerBullets[j].pos.x), BOSS_X), + fix32Sub(fix16ToFix32(playerBullets[j].pos.y), BOSS_Y)); + if(bossCollisionDistance < BOSS_COLLIDE_OFFSET){ + spawnExplosion(fix16ToInt(playerBullets[j].pos.x), fix16ToInt(playerBullets[j].pos.y), TRUE); + hitBoss(); + removePlayerBullet(j); + } + } +} + +void finishBoss(){ + bossActive = FALSE; + bossLoaded = FALSE; + zoneOver = TRUE; + bossClock = BOSS_SWITCH_TIME; + for(s8 x = 0; x < 8; x++) for(s8 y = 0; y < 8; y++) VDP_setTileMapXY(BG_A, TILE_ATTR_FULL(PAL1, 0, 0, 0, 11), x + 12, y + 4); + for(s8 x = 0; x < BOSS_TILE_COUNT; x++) VDP_setTileMapXY(BG_A, TILE_ATTR_FULL(PAL1, 0, 0, 0, 11), x + 1, BOSS_TILE_Y); + + + // VDP_drawImageEx(BG_A, &chromeEnemy, TILE_ATTR_FULL(PAL1, 1, 0, 0, 190), 1, BOSS_TILE_Y, 0, DMA_QUEUE); +} + + +// loop + +void loadBoss(){ + bossType++; + // bossType = 4; + switch(bossType){ + case 1: + bossHealth = 100; + break; + case 2: + bossHealth = 150; + break; + case 3: + bossHealth = 150; + break; + case 4: + bossHealth = 250; + break; + } + bossLoaded = FALSE; + bossActive = TRUE; + bossMax = bossHealth; +} + +void updateBoss(){ + if(bossActive) { + if(bossLoaded){ + collideBoss(); + if(!gameOver) shootBoss(); + } else if(bossClock == BOSS_LOAD_TIME){ + VDP_drawImageEx(BG_A, + (bossType == 1 ? &roll : (bossType == 2 ? &waffle : (bossType == 3 ? &cake : &parfait))), + TILE_ATTR_FULL(PAL1, 0, 0, 0, 92), 12, 4, 0, DMA_QUEUE); + bossLoaded = TRUE; + bossClock = -1; + } + bossClock++; + if(bossClock >= 600) bossClock = 0; + } +} \ No newline at end of file diff --git a/src/boss.h b/src/boss.h new file mode 100644 index 0000000..e955811 --- /dev/null +++ b/src/boss.h @@ -0,0 +1,47 @@ +#define BOSS_LOAD_TIME 15 + +#define BOSS_X FIX32(16 * 8) +#define BOSS_Y FIX32(8 * 8) +#define BOSS_B_X fix32ToFix16(BOSS_X) +#define BOSS_B_Y fix32ToFix16(BOSS_Y) +#define BOSS_X_OFFSET FIX16(8 * 10) +#define BOSS_X_OFFSET_NEG fix16Sub(FIX16(0), BOSS_X_OFFSET) + +#define BOSS_COLLIDE_OFFSET FIX32(40) + +#define BOSS_SWITCH_TIME -90 + +s16 bossClock, bossInt1, bossInt2, bossInt3, bossType, bossHealth, bossMax; +f16 bossFix1, bossFix2; +s32 bossCollisionDistance; + +bool hitBossPatterns[8]; +bool bossActive, bossLoaded; + +void loadBoss(), + + bossPatternOne(), + bossPatternTwo(), + + bossPatternThree(), + bossPatternFour(), + bossPatternFive(), + + bossPatternSix(), + bossPatternSeven(), + bossPatternEight(), + + bossPatternNine(), + bossPatternTen(), + bossPatternEleven(), + bossPatternTwelve(), + bossPatternThirteen(), + + hitBossPattern(u8), + + drawBoss(), + collideBoss(), + hitBoss(), + finishBoss(), + shootBoss(), + updateBoss(); \ No newline at end of file diff --git a/src/centipede.c b/src/centipede.c new file mode 100644 index 0000000..0c1ab4e --- /dev/null +++ b/src/centipede.c @@ -0,0 +1,195 @@ +#include +#include + +#include "main.h" +#include "enemies.h" +#include "centipede.h" +#include "player.h" +#include "pod.h" +#include "explosion.h" + + +// spawn + +void spawnCentipede(){} + + +// movement + +void moveCentipede(s16 i){ + if(centipedes[i].flipping){ + centipedes[i].pos.y = fix16Add(centipedes[i].pos.y, (centipedes[i].flippedY ? fix16Sub(FIX16(0), centipedes[i].speed) : centipedes[i].speed)); + centipedes[i].flipClock++; + if((centipedes[i].flippedY && centipedes[i].pos.y <= centipedes[i].nextY) || (!centipedes[i].flippedY && centipedes[i].pos.y >= centipedes[i].nextY)){ + centipedes[i].flipping = FALSE; + centipedes[i].pos.y = centipedes[i].nextY; + } + } else { + centipedes[i].pos.x = fix16Add(centipedes[i].pos.x, (centipedes[i].flippedX ? fix16Sub(FIX16(0), centipedes[i].speed) : centipedes[i].speed)); + if(centipedes[i].pos.x >= CENTIPEDE_LIMIT_RIGHT || centipedes[i].pos.x <= CENTIPEDE_LIMIT_LEFT){ + centipedes[i].flippedX = centipedes[i].pos.x >= CENTIPEDE_LIMIT_RIGHT; + if(!centipedes[i].flippedY && centipedes[i].pos.y >= CENTIPEDE_LIMIT_BOTTOM){ + centipedes[i].flippedY = TRUE; + if(i == lastCentipede && lastCentipede < centipedeCount){ + struct podSpawner pSpawn = { + .x = FIX16(fix16ToInt(centipedes[i].pos.x) / 16 * 16), + .y = FIX16(fix16ToInt(centipedes[i].pos.y) / 16 * 16), + .random = FALSE + }; + spawnPod(pSpawn); + } + // can add another head + }else if(centipedes[i].flippedY && centipedes[i].pos.y <= CENTIPEDE_LIMIT_TOP) centipedes[i].flippedY = FALSE; + centipedes[i].flipping = TRUE; + centipedes[i].nextY = centipedes[i].flippedY ? fix16Sub(centipedes[i].pos.y, FIX16(16)) : fix16Add(centipedes[i].pos.y, FIX16(16)); + } + if(centipedes[i].flipClock > 0) centipedes[i].flipClock = 0; + } +} + + +// change sprite + +void animateCentipede(s16 i){ + if(centipedes[i].definition > 0){ + switch(centipedes[i].definition){ + case 1: + SPR_setDefinition(centipedes[i].image, &imgGumdropRed); + break; + case 2: + SPR_setDefinition(centipedes[i].image, &imgGumdropGreen); + break; + case 3: + SPR_setDefinition(centipedes[i].image, &imgGumdropBlue); + break; + } + centipedes[i].definition = 0; + } +} + + +// collision against centipede + +void hitCentipede(s16 i){ + spawnExplosion(fix16ToInt(centipedes[i].pos.x), fix16ToInt(centipedes[i].pos.y), TRUE); + centipedes[i].health -= CENTIPEDE_HIT; + XGM_startPlayPCM(random() % 2 < 1 ? SFX_EXPLOSION_1 : SFX_EXPLOSION_2, 1, SOUND_PCM_CH4); + currentScore += 7; + if(centipedes[i].health < 0){ + struct podSpawner pSpawn = { + .x = FIX16(fix16ToInt(centipedes[i].pos.x) / 16 * 16), + .y = FIX16(fix16ToInt(centipedes[i].pos.y) / 16 * 16), + .random = FALSE + }; + spawnPod(pSpawn); + destroyCentipede(i); + currentScore += currentZone >= 10 ? 2500 : 1500; + XGM_startPlayPCM(SFX_EXPLOSION_3, 2, SOUND_PCM_CH4); + } else if(centipedes[i].health < 33) centipedes[i].definition = 1; + else if(centipedes[i].health < 67) centipedes[i].definition = 2; +} + +void turnCentipede(s16 i, s16 j){ + centipedes[i].flippedX = centipedes[i].flippedX ? FALSE : TRUE; + centipedes[i].turning = TRUE; + centipedes[i].turnClock = 0; + if(centipedes[i].pos.x < pods[j].pos.x) centipedes[i].pos.x = fix16Sub(pods[j].pos.x, FIX16(8)); + else if(centipedes[i].pos.x >= pods[j].pos.x) centipedes[i].pos.x = fix16Add(pods[j].pos.x, FIX16(8)); +} + +void collideCentipede(s16 i){ + // against pods + for(s16 j = 0; j < POD_COUNT; j++) if(pods[j].active) { + centipedePodCheck = getApproximatedDistance( + fix16ToFix32(centipedes[i].pos.x) - fix16ToFix32(pods[j].pos.x), + fix16ToFix32(centipedes[i].pos.y) - fix16ToFix32(pods[j].pos.y)); + if(centipedePodCheck < CENTIPEDE_POD_OFFSET) turnCentipede(i, j); + } + + if(bombing && centipedes[i].clock % 20 == 0) hitCentipede(i); + // against player bullet + centipedeCollided = FALSE; + for(s16 j = 0; j < PLAYER_BULLET_LIMIT; j++) if(playerBullets[j].active) { + if(fix16Sub(playerBullets[j].pos.y, CENTIPEDE_BULLET_OFFSET) <= fix16Add(centipedes[i].pos.y, CENTIPEDE_COLLIDE_OFFSET) && + fix16Add(playerBullets[j].pos.y, CENTIPEDE_BULLET_OFFSET) >= fix16Sub(centipedes[i].pos.y, CENTIPEDE_COLLIDE_OFFSET) && + fix16Sub(playerBullets[j].pos.x, CENTIPEDE_BULLET_OFFSET) <= fix16Add(centipedes[i].pos.x, CENTIPEDE_COLLIDE_OFFSET) && + fix16Add(playerBullets[j].pos.x, CENTIPEDE_BULLET_OFFSET) >= fix16Sub(centipedes[i].pos.x, CENTIPEDE_COLLIDE_OFFSET)){ + centipedeCollided = TRUE; + hitCentipede(i); + removePlayerBullet(j); + } + } + // against player + if(centipedes[i].clock % 4 == 0 && (!centipedeCollided && centipedes[i].pos.y >= fix16Sub(playerPos.y, FIX16(32)))){ + if(fix16Sub(playerPos.y, CENTIPEDE_PLAYER_OFFSET) <= fix16Add(centipedes[i].pos.y, CENTIPEDE_COLLIDE_OFFSET) && + fix16Add(playerPos.y, CENTIPEDE_PLAYER_OFFSET) >= fix16Sub(centipedes[i].pos.y, CENTIPEDE_COLLIDE_OFFSET) && + fix16Sub(playerPos.x, CENTIPEDE_PLAYER_OFFSET) <= fix16Add(centipedes[i].pos.x, CENTIPEDE_COLLIDE_OFFSET) && + fix16Add(playerPos.x, CENTIPEDE_PLAYER_OFFSET) >= fix16Sub(centipedes[i].pos.x, CENTIPEDE_COLLIDE_OFFSET)){ + // hitCentipede(i); + // turnCentipede(i); + // hit player here + } + } +} + +void destroyCentipede(s16 i){ + centipedes[i].active = FALSE; + centipedes[i].flippedX = FALSE; + centipedes[i].flippedY = FALSE; + centipedes[i].flipping = FALSE; + centipedes[i].flag1 = FALSE; + centipedes[i].pos.x = CENTIPEDE_DUMP_X; + centipedes[i].pos.y = CENTIPEDE_DUMP_Y; + SPR_releaseSprite(centipedes[i].image); +} + + +// loop + +void loadCentipede(){ + centipedeCount = 4; + if(currentZone >= 5) centipedeCount++; + if(currentZone >= 10) centipedeCount++; + if(currentZone >= 15) centipedeCount++; + for(s16 i = 0; i < centipedeCount; i++){ + centipedes[i].active = TRUE; + centipedes[i].pos.x = FIX16(16 + 16 * i); + centipedes[i].pos.y = CENTIPEDE_LIMIT_TOP; + // centipedes[i].speed = FIX16(currentZone >= 10 ? 4 : 2); + centipedes[i].speed = FIX16(2); + centipedes[i].image = SPR_addSprite(&imgGumdropRed, fix16ToInt(centipedes[i].pos.x), fix16ToInt(centipedes[i].pos.y), TILE_ATTR(PAL1, 0, FALSE, FALSE)); + centipedes[i].definition = 3; + centipedes[i].health = i % 2 == 0 ? 80 : 100; + centipedes[i].opposite = i % 2 == 1; + SPR_setDepth(centipedes[i].image, 3); + } +} + +void resetCentipede(){ + for(s16 i = 0; i < centipedeCount; i++) destroyCentipede(i); +} + +void updateCentipede(){ + zoneOverCheck = TRUE; + tempLastCentipede = 0; + for(s16 i = 0; i < centipedeCount; i++) if(centipedes[i].active) { + tempLastCentipede++; + zoneOverCheck = FALSE; + moveCentipede(i); + collideCentipede(i); + animateCentipede(i); + if(centipedes[i].turning){ + centipedes[i].turnClock++; + if(centipedes[i].turnClock >= 30) centipedes[i].turning = FALSE; + } + SPR_setPosition(centipedes[i].image, fix16ToInt(centipedes[i].pos.x) - CENTIPEDE_OFFSET, fix16ToInt(centipedes[i].pos.y) - CENTIPEDE_OFFSET); + if(centipedes[i].opposite){ + if(centipedes[i].clock % 40 == 0) SPR_setFrame(centipedes[i].image, 1); + else if(centipedes[i].clock % 40 == 20) SPR_setFrame(centipedes[i].image, 0); + } + centipedes[i].clock++; + if(centipedes[i].clock >= 600) centipedes[i].clock = 120; + } + lastCentipede = tempLastCentipede; + if(zoneOverCheck) zoneOver = TRUE; +} diff --git a/src/centipede.h b/src/centipede.h new file mode 100644 index 0000000..3f73c0e --- /dev/null +++ b/src/centipede.h @@ -0,0 +1,43 @@ +#define CENTIPEDE_MAX 8 +#define CENTIPEDE_OFFSET 8 +#define CENTIPEDE_LIMIT_LEFT FIX16(8) +#define CENTIPEDE_LIMIT_RIGHT FIX16(GAME_WIDTH - 8) +#define CENTIPEDE_LIMIT_TOP FIX16(32) +#define CENTIPEDE_LIMIT_BOTTOM FIX16(GAME_HEIGHT - 16) + +#define CENTIPEDE_COLLIDE_OFFSET FIX16(8) +#define CENTIPEDE_BULLET_OFFSET FIX16(8) +#define CENTIPEDE_PLAYER_OFFSET FIX16(2) +#define CENTIPEDE_POD_OFFSET FIX32(4) + +#define CENTIPEDE_DUMP_X FIX16(GAME_WIDTH + 64) +#define CENTIPEDE_DUMP_Y FIX16(0 - 64) + +#define CENTIPEDE_HIT 6 +// #define CENTIPEDE_HIT 100 + +struct centipede { + Sprite* image; + Vect2D_f16 pos; + bool flippedX, flippedY, flipping, active, flag1, turning, opposite; + s16 clock, flipClock, definition, health, turnClock; + f16 speed, nextY; +}; + +s16 centipedeCount, lastCentipede, tempLastCentipede; +s32 centipedePodCheck; +f16 centipedeSpeed; + +struct centipede centipedes[CENTIPEDE_MAX]; + +bool centipedeCollided, zoneOverCheck; + +void loadCentipede(), + moveCentipede(s16), + collideCentipede(s16), + animateCentipede(s16), + destroyCentipede(s16), + splitCentipede(s16), + turnCentipede(s16, s16), + resetCentipede(), + updateCentipede(); \ No newline at end of file diff --git a/src/chrome.c b/src/chrome.c new file mode 100644 index 0000000..983195b --- /dev/null +++ b/src/chrome.c @@ -0,0 +1,178 @@ +#include +#include + +#include "main.h" +#include "chrome.h" +#include "boss.h" +#include "player.h" + + +// lives + +void loadChromeLives(){ + VDP_drawImageEx(BG_A, &chromePlayer, TILE_ATTR_FULL(PAL1, 1, 0, 0, 160), 1, 1, 0, DMA_QUEUE); +} + +void updateChromePlayerLives(){ + if(chromePlayerLives != playerLives){ + for(s8 x = 0; x < 6; x++){ + VDP_setTileMapXY(BG_A, TILE_ATTR_FULL(PAL1, 0, 0, 0, 11), x + 6, 1); + if(x < playerLives) VDP_drawText("#", x + 6, 1); + } + chromePlayerLives = playerLives; + } +} + + +// bombs + +void updateChromePlayerBombs(){ + if(chromePlayerBombs != playerBombs){ + for(s8 x = 0; x < 6; x++){ + VDP_setTileMapXY(BG_A, TILE_ATTR_FULL(PAL1, 0, 0, 0, 11), x + 1, PLAYER_BOMBS_Y); + if(x < playerBombs) VDP_drawText("*", x + 1, PLAYER_BOMBS_Y); + } + chromePlayerBombs = playerBombs; + } +} + + +// score + +void updateChromeScore(){ + chromeCurrentScore = currentScore; + intToStr(chromeCurrentScore, chromeScoreStr, 8); + VDP_drawText(chromeScoreStr, 12, 1); +} + + +// zone + +void loadChromeZone(){ + intToStr(currentZone, zoneHudStr, 2); + VDP_drawText(zoneHudStr, 29, 1); + VDP_drawImageEx(BG_A, &chromeStage, TILE_ATTR_FULL(PAL1, 1, 0, 0, 170), 24, 1, 0, DMA_QUEUE); +} + + +// states + +void loadChromeZoneOver(){ + chromePlayerLives = 0; + chromePlayerBombs = 0; + intToStr(currentZone, currentZoneStr, 2); + VDP_drawText("stage", 7, 8); + VDP_drawText(currentZoneStr, 13, 8); + VDP_drawText("complete!", 16, 8); + VDP_drawText("NO-MISS", 7, 12); + VDP_drawText(noMiss ? (currentZone >= 10 ? "35000" : "25000") : "00000", 20, 12); + VDP_drawText("STAGE", 7, 14); + VDP_drawText(currentZone >= 10 ? "15000" : "10000", 20, 14); + if(currentZone % 5 == 0){ + VDP_drawText("BOSS", 7, 16); + VDP_drawText(currentZone >= 10 ? "30000" : "20000", 20, 16); + currentScore += currentZone >= 10 ? 30000 : 20000; + } + if(currentZone == 10) XGM_stopPlay(); + currentZone++; + VDP_drawText("next stage", 7, 21); + loadedZoneOver = TRUE; + if(noMiss) currentScore += currentZone >= 10 ? 35000 : 25000; + currentScore += currentZone >= 10 ? 15000 : 10000; + updateChromeScore(); + XGM_startPlayPCM(SFX_ZONE_OVER, 1, SOUND_PCM_CH2); +} + +void updateChromeZoneOver(){ // what the fuck am i on to do this + strcpy(zoneOverTime, zoneOverClock >= 180 ? "3" : (zoneOverClock >= 120 ? "2" : (zoneOverClock >= 60 ? "1" : "0"))); + strcat(zoneOverTime, ";"); + if(zoneOverClock % 60 < 10) strcat(zoneOverTime, "0"); + else if(zoneOverClock % 60 < 20) strcat(zoneOverTime, "1"); + else if(zoneOverClock % 60 < 30) strcat(zoneOverTime, "2"); + else if(zoneOverClock % 60 < 40) strcat(zoneOverTime, "3"); + else if(zoneOverClock % 60 < 50) strcat(zoneOverTime, "4"); + else if(zoneOverClock % 60 < 60) strcat(zoneOverTime, "5"); + switch(zoneOverClock % 6){ + case 0: strcat(zoneOverTime, "0"); break; + case 1: strcat(zoneOverTime, "1"); break; + case 2: strcat(zoneOverTime, "2"); break; + case 3: strcat(zoneOverTime, "3"); break; + case 4: strcat(zoneOverTime, "4"); break; + case 5: strcat(zoneOverTime, "5"); break; + case 6: strcat(zoneOverTime, "6"); break; + case 7: strcat(zoneOverTime, "7"); break; + case 8: strcat(zoneOverTime, "8"); break; + case 9: strcat(zoneOverTime, "9"); break; + } + VDP_drawText(zoneOverTime, 21, 21); + zoneOverClock--; + if(zoneOverClock <= 0) nextZone(); +} + +void loadChromeGameOver(bool beatIt){ + XGM_stopPlay(); + loadedChromeGameOver = TRUE; + VDP_drawText(beatIt ? "beat game!" : "game over!", 11, 10); + VDP_drawText(currentScore > highScore ? "NEW HI SCORE" : "FINAL SCORE;", 10, 13); + VDP_drawText(chromeScoreStr, 12, 15); + if(beatIt){ + VDP_drawText("special thanks", 9, 19); + VDP_drawText("TOUHOU GAMEDEV DISCORD", 5, 21); + } else VDP_drawText("press any button", 8, 18); + if(currentScore > highScore) highScore = currentScore; + XGM_startPlayPCM(beatIt ? SFX_BEAT_GAME : SFX_GAME_OVER, 2, SOUND_PCM_CH2); +} + +void loadChromeBeatGame(){ + gameOver = TRUE; + loadChromeGameOver(TRUE); +} + + +// boss + +void updateChromeBoss(){ + if(bossActive){ + if(lastBossHealth != bossHealth){ + VDP_drawImageEx(BG_A, &chromeEnemy, TILE_ATTR_FULL(PAL1, 1, 0, 0, 190), 1, BOSS_TILE_Y, 0, DMA_QUEUE); + bossLimit = fix16Div(fix16Mul(fix16Div(FIX16(bossHealth), FIX16(bossMax)), BOSS_TILE_PX), 8); + for(s16 x = 0; x < BOSS_TILE_COUNT; x++){ + if(bossHealth <= 1 || FIX16(x) >= bossLimit){ + VDP_setTileMapXY(BG_A, TILE_ATTR_FULL(PAL1, 0, 0, 0, 11), x + BOSS_TILE_X, BOSS_TILE_Y); + } + } + for(s16 x = 0; x < BOSS_TILE_COUNT; x++){ + if(bossHealth > 1 && FIX16(x) < bossLimit) VDP_setTileMapXY(BG_A, TILE_ATTR_FULL(PAL1, 1, 0, 0, 8), x + BOSS_TILE_X, BOSS_TILE_Y); + } + lastBossHealth = bossHealth; + } + } +} + + +// loop + +void loadChrome(){ + VDP_loadTileSet(bossBar.tileset, 8, DMA); + updateChromeScore(); + loadChromeZone(); + loadChromeLives(); + zoneOverClock = ZONE_OVER_CHROME_LIMIT; +} + +void updateChrome(){ + if(zoneStarting) loadChrome(); + else { + if(zoneOver){ + if(!loadedZoneOver) currentZone == 20 ? loadChromeBeatGame() : loadChromeZoneOver(); + updateChromeZoneOver(); + } else if(gameOver){ + if(!loadedChromeGameOver) loadChromeGameOver(FALSE); + } else { + updateChromePlayerLives(); + updateChromePlayerBombs(); + updateChromeBoss(); + if(chromeCurrentScore < currentScore) updateChromeScore(); + } + } +} \ No newline at end of file diff --git a/src/chrome.h b/src/chrome.h new file mode 100644 index 0000000..232d469 --- /dev/null +++ b/src/chrome.h @@ -0,0 +1,36 @@ +// #define ZONE_OVER_CHROME_LIMIT 1 +#define ZONE_OVER_CHROME_LIMIT 240 +// #define ZONE_OVER_CHROME_LIMIT 60 + +#define BOSS_TILE_X 6 +#define BOSS_TILE_Y 3 +#define BOSS_TILE_COUNT 25 +#define BOSS_TILE_PX BOSS_TILE_COUNT * 8 + +#define PLAYER_BOMBS_Y 26 + +s16 frameTileIndex, zoneOverClock, zoneOverStage, lastBossHealth, bossTileIndex; +s8 chromePlayerLives, chromePlayerBombs; +s32 chromeCurrentScore; + +fix16 bossLimit, lastBossLimit; + +bool loadedZoneOver, loadedChromeGameOver; + +char currentZoneStr[2], + zoneHudStr[2], + currentZoneApp[1], + zoneOverTime[8], + chromeScoreStr[10]; + +void loadChrome(), + loadChromeZoneOver(), + loadChromeLives(), + loadChromeGameOver(bool), + updateChromeZoneOver(), + updateChromeScore(), + loadChromeBeatGame(), + updateChromePlayerLives(), + updateChromePlayerBombs(), + updateChromeBoss(), + updateChrome(); \ No newline at end of file diff --git a/src/controls.c b/src/controls.c new file mode 100644 index 0000000..fffd3ee --- /dev/null +++ b/src/controls.c @@ -0,0 +1,16 @@ +#include +#include "controls.h" + +void updateControls(u16 joy, u16 changed, u16 state){ + if(joy == JOY_1){ + if(changed){} + controls.left = (state & BUTTON_LEFT); + controls.right = (state & BUTTON_RIGHT); + controls.up = (state & BUTTON_UP); + controls.down = (state & BUTTON_DOWN); + controls.a = (state & BUTTON_A); + controls.b = (state & BUTTON_B); + controls.c = (state & BUTTON_C); + controls.start = (state & BUTTON_START); + } +} \ No newline at end of file diff --git a/src/controls.h b/src/controls.h new file mode 100644 index 0000000..b46e76e --- /dev/null +++ b/src/controls.h @@ -0,0 +1,7 @@ +struct controls { + bool left, right, up, down, a, b, c, x, y, z, start, mode; +}; + +struct controls controls; + +void updateControls(u16, u16, u16); \ No newline at end of file diff --git a/src/enemies.c b/src/enemies.c new file mode 100644 index 0000000..2674560 --- /dev/null +++ b/src/enemies.c @@ -0,0 +1,177 @@ +#include +#include + +#include "main.h" +#include "enemies.h" +#include "player.h" +#include "yin.h" +#include "centipede.h" +#include "pod.h" +#include "boss.h" + + +// bullets + +void spawnEnemyBullet(struct bulletSpawner spawner, void(*updater)){ + s16 i = -1; + for(s16 h = 0; h < ENEMY_BULLET_LIMIT; h++) if(!bullets[h].active && i == -1) i = h; + if(i > -1 && !gameOver){ + bullets[i].active = TRUE; + bullets[i].grazed = FALSE; + bullets[i].pos.x = spawner.x; + bullets[i].pos.y = spawner.y; + bullets[i].speed = spawner.speed ? spawner.speed : FIX16(2); + bullets[i].angle = spawner.angle ? spawner.angle : 0; + bullets[i].flag1 = spawner.flag1 ? TRUE : FALSE; + bullets[i].flag2 = spawner.flag2 ? TRUE : FALSE; + bullets[i].flag3 = spawner.flag3 ? TRUE : FALSE; + bullets[i].flag4 = spawner.flag4 ? TRUE : FALSE; + bullets[i].int1 = spawner.int1 ? spawner.int1 : 0; + bullets[i].int2 = spawner.int2 ? spawner.int2 : 0; + bullets[i].int3 = spawner.int3 ? spawner.int3 : 0; + bullets[i].int4 = spawner.int4 ? spawner.int4 : 0; + bullets[i].velocity.x = spawner.velocityX ? spawner.velocityX : fix16Mul(cosFix16(spawner.angle), spawner.speed); + bullets[i].velocity.y = spawner.velocityY ? spawner.velocityY : fix16Mul(sinFix16(spawner.angle), spawner.speed); + bullets[i].updater = updater; + bullets[i].clock = 0; + switch(spawner.type){ + case 1: bullets[i].image = SPR_addSprite(&imgSmallRedBullet, bullets[i].pos.x, bullets[i].pos.y, TILE_ATTR(PAL1, 0, FALSE, FALSE)); break; + case 2: bullets[i].image = SPR_addSprite(&imgBigRedBullet, bullets[i].pos.x, bullets[i].pos.y, TILE_ATTR(PAL1, 0, FALSE, FALSE)); break; + case 3: bullets[i].image = SPR_addSprite(&imgSmallBlueBullet, bullets[i].pos.x, bullets[i].pos.y, TILE_ATTR(PAL1, 0, FALSE, FALSE)); break; + case 4: bullets[i].image = SPR_addSprite(&imgBigBlueBullet, bullets[i].pos.x, bullets[i].pos.y, TILE_ATTR(PAL1, 0, FALSE, FALSE)); break; + case 5: bullets[i].image = SPR_addSprite(&imgSmallPinkBullet, bullets[i].pos.x, bullets[i].pos.y, TILE_ATTR(PAL1, 0, FALSE, FALSE)); break; + case 6: bullets[i].image = SPR_addSprite(&imgBigPinkBullet, bullets[i].pos.x, bullets[i].pos.y, TILE_ATTR(PAL1, 0, FALSE, FALSE)); break; + } + if(spawner.type % 2 == 0){ + bullets[i].xOffset = 8; + bullets[i].yOffset = 8; + SPR_setDepth(bullets[i].image, 2); + } else if(spawner.type % 2 == 1){ + bullets[i].xOffset = 4; + bullets[i].yOffset = 4; + SPR_setDepth(bullets[i].image, 1); + } + SPR_setPosition(bullets[i].image, fix16ToInt(bullets[i].pos.x) - bullets[i].xOffset, fix16ToInt(bullets[i].pos.y) - bullets[i].yOffset); + } +} + +void checkEnemyBulletCollision(s16 i){ + enemyBulletCollisionDistance = getApproximatedDistance( + fix16ToFix32(playerPos.x) - fix16ToFix32(bullets[i].pos.x), + fix16ToFix32(playerPos.y) - fix16ToFix32(bullets[i].pos.y)); + if(enemyBulletCollisionDistance < intToFix32(bullets[i].xOffset) && !playerRecovering){ + spawnExplosion(fix16ToInt(bullets[i].pos.x), fix16ToInt(bullets[i].pos.y), TRUE); + destroyEnemyBullet(i); + hitPlayer = TRUE; + killBullets = TRUE; + // SND_startPlayPCM_XGM(SFX_EXPLOSION2, 15, SOUND_PCM_CH2); + } else if(!bullets[i].grazed){ + bullets[i].grazed = TRUE; + currentScore += 50; + // SND_startPlayPCM_XGM(SFX_GRAZE, 15, SOUND_PCM_CH2); + } +} + +void updateEnemyBullet(s16 i){ + bullets[i].pos.x = fix16Add(bullets[i].pos.x, bullets[i].velocity.x); + bullets[i].pos.y = fix16Add(bullets[i].pos.y, bullets[i].velocity.y); + bullets[i].updater(i); + enemyBulletCount++; + bullets[i].clock++; + if(bullets[i].pos.x < FIX16(0 - bullets[i].xOffset) || bullets[i].pos.x > FIX16(256 + bullets[i].xOffset) || + bullets[i].pos.y < FIX16(0 - bullets[i].yOffset) || bullets[i].pos.y > FIX16(224 + bullets[i].yOffset) || + killBullets || bullets[i].clock >= 600 || zoneOver){ + destroyEnemyBullet(i); + } +} + +void destroyEnemyBullet(s16 i){ + SPR_releaseSprite(bullets[i].image); + bullets[i].active = FALSE; + bullets[i].clock = 0; + bullets[i].pos.x = FIX16(GAME_WIDTH); + bullets[i].pos.y = FIX16(32); +} + +void drawEnemyBullet(s16 i){ + SPR_setPosition(bullets[i].image, + fix16ToInt(bullets[i].pos.x) - bullets[i].xOffset, fix16ToInt(bullets[i].pos.y) - bullets[i].yOffset); + if(bullets[i].pos.y > fix16Sub(playerPos.y, ENEMY_BULLET_COLLISION_CHECK) && + bullets[i].pos.y < fix16Add(playerPos.y, ENEMY_BULLET_COLLISION_CHECK) && + bullets[i].pos.x > fix16Sub(playerPos.x, ENEMY_BULLET_COLLISION_CHECK) && + bullets[i].pos.x < fix16Add(playerPos.x, ENEMY_BULLET_COLLISION_CHECK)){ + checkEnemyBulletCollision(i); + } +} + + +// utils + +void eUpdate(s16 i){if(i){}} + +void updateEnemyBulletVelocity(s16 i){ + bullets[i].velocity.x = fix16Mul(cosFix16(bullets[i].angle), bullets[i].speed); + bullets[i].velocity.y = fix16Mul(sinFix16(bullets[i].angle), bullets[i].speed); +} + +fix16 honeEnemyBullet(fix16 x, fix16 y, s16 speed, s16 lerp, bool isX){ + honeX = playerPos.x; + honeY = playerPos.y; + if(lerp > 0){ + honeX = fix16Add(fix16Sub(honeX, FIX16(lerp)), FIX16(random() % (lerp * 2))); + honeY = fix16Add(fix16Sub(honeY, FIX16(lerp)), FIX16(random() % (lerp * 2))); + } + honeSpeed = fix32ToFix16(getApproximatedDistance(fix16ToFix32(x) - fix16ToFix32(honeX), fix16ToFix32(y) - fix16ToFix32(honeY))); + return isX ? fix16Mul(fix16Div(fix16Sub(honeX, x), honeSpeed), FIX16(speed)) : fix16Mul(fix16Div(fix16Sub(honeY, y), honeSpeed), FIX16(speed)); +} + + +// kill bullets dramatically + +void updateKillBullets(){ + if(enemyBulletCount >= ENEMY_BULLET_LIMIT) killBullets = TRUE; + if(killBullets){ + if(killBulletsClock % 4 == 0 && killBulletsClock < 20){ + spawnExplosion(32 + random() % 192, 32 + random() % 128, FALSE); + } + killBulletsClock++; + if(killBulletsClock >= 30){ + killBullets = FALSE; + killBulletsClock = 0; + } + } +} + + +// loop + +void loadEnemies(){ + loadYins(); + loadCentipede(); + loadPod(); +} + +void resetEnemies(){ + resetYins(); + resetCentipede(); + resetPod(); + for(s16 i = 0; i < ENEMY_BULLET_LIMIT; i++) destroyEnemyBullet(i); +} + +void updateEnemies(){ + if(zoneStarting && !bossActive) loadEnemies(); + else { + if(bossActive) updateBoss(); + else { + updateYins(); + updateCentipede(); + updatePod(); + } + if(gameClock % 2 == 0){ + enemyBulletCount = 0; + // for(s16 i = 0; i < ENEMY_LIMIT; i++) if(enemies[i].active) updateEnemy(i); + for(s16 i = 0; i < ENEMY_BULLET_LIMIT; i++) if(bullets[i].active) updateEnemyBullet(i); + updateKillBullets(); + } else for(s16 i = 0; i < ENEMY_BULLET_LIMIT; i++) if(bullets[i].active) drawEnemyBullet(i); + } +} \ No newline at end of file diff --git a/src/enemies.h b/src/enemies.h new file mode 100644 index 0000000..a3234fc --- /dev/null +++ b/src/enemies.h @@ -0,0 +1,39 @@ +#define ENEMY_BULLET_LIMIT 64 +#define ENEMY_BULLET_COLLISION_CHECK FIX16(14) + +struct enemyBullet { + bool active, flag1, flag2, flag3, flag4, grazed; + Vect2D_f16 pos, velocity; + fix16 speed; + Sprite* image; + s16 xOffset, yOffset, clock, angle, int1, int2, int3, int4; + void (*updater)(s16); +}; + +struct bulletSpawner { + bool flag1, flag2, flag3, flag4; + fix16 speed, velocityX, velocityY, x, y; + s16 angle, type, int1, int2, int3, int4; +}; + +struct enemyBullet bullets[ENEMY_BULLET_LIMIT]; + +bool killBullets; + +s32 enemyBulletCollisionDistance; +s16 killBulletsClock, enemyBulletCount; +s8 podBulletSpeed, yinBulletSpeed; + +fix16 honeX, honeY, honeSpeed, + honeEnemyBullet(fix16, fix16, s16, s16, bool); + +void loadEnemies(), + spawnEnemyBullet(struct bulletSpawner, void *), + checkEnemyBulletCollision(s16), + updateEnemyBullet(s16), + drawEnemyBullet(s16), + updateKillBullets(), + eUpdate(s16), + destroyEnemyBullet(s16), + resetEnemies(), + updateEnemies(); \ No newline at end of file diff --git a/src/explosion.c b/src/explosion.c new file mode 100644 index 0000000..316c5cb --- /dev/null +++ b/src/explosion.c @@ -0,0 +1,28 @@ +#include +#include +#include "main.h" +#include "explosion.h" + +void loadExplosion(){ + explosionClock = 0; + explosionClockPlayerShot = 0; + explosionImage = SPR_addSprite(&imgExplosionPlayer, EXPLOSION_DUMP_X, EXPLOSION_DUMP_Y, TILE_ATTR(PAL1, 0, FALSE, FALSE)); + explosionImagePlayerShot = SPR_addSprite(&imgExplosionPlayer, EXPLOSION_DUMP_X, EXPLOSION_DUMP_Y, TILE_ATTR(PAL1, 0, FALSE, FALSE)); + SPR_setDepth(explosionImage, 3); + SPR_setDepth(explosionImagePlayerShot, 1); +} + +void spawnExplosion(s16 x, s16 y, bool isPlayerShot){ + SPR_setPosition(isPlayerShot ? explosionImagePlayerShot : explosionImage, x - EXPLOSION_SPRITE_OFFSET, y - EXPLOSION_SPRITE_OFFSET); + isPlayerShot ? (explosionClockPlayerShot = 0) : (explosionClock = 0); + // XGM_startPlayPCM(random() % 2 < 1 ? SFX_EXPLOSION_1 : SFX_EXPLOSION_2, 1, SOUND_PCM_CH4); +} + +void updateExplosion(){ + if(explosionClock >= EXPLOSION_LIMIT || paused) SPR_setPosition(explosionImage, EXPLOSION_DUMP_X, EXPLOSION_DUMP_Y); + if(explosionClockPlayerShot >= EXPLOSION_LIMIT || paused) SPR_setPosition(explosionImagePlayerShot, EXPLOSION_DUMP_X, EXPLOSION_DUMP_Y); + explosionClock++; + explosionClockPlayerShot++; + if(explosionClock >= 600) explosionClock = 60; + if(explosionClockPlayerShot >= 600) explosionClockPlayerShot = 60; +} \ No newline at end of file diff --git a/src/explosion.h b/src/explosion.h new file mode 100644 index 0000000..e6e8033 --- /dev/null +++ b/src/explosion.h @@ -0,0 +1,15 @@ +#define EXPLOSION_DUMP_X GAME_WIDTH + 32 +#define EXPLOSION_DUMP_Y GAME_HEIGHT + 64 + +#define EXPLOSION_SPRITE_OFFSET 12 +#define EXPLOSION_LIMIT 20 + +s16 explosionClock, + explosionClockPlayerShot; + +Sprite* explosionImage; +Sprite* explosionImagePlayerShot; + +void loadExplosion(), + spawnExplosion(s16, s16, bool), + updateExplosion(); \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..c543dfd --- /dev/null +++ b/src/main.c @@ -0,0 +1,140 @@ +#include +#include + +#include "main.h" +#include "controls.h" +#include "chrome.h" +#include "start.h" +#include "boss.h" +#include "background.h" +#include "enemies.h" +#include "player.h" +#include "explosion.h" + +void loadResources(){ + VDP_loadFont(font.tileset, DMA); + VDP_loadTileSet(half.tileset, 10, DMA); + VDP_loadTileSet(least.tileset, 11, DMA); + VDP_loadTileSet(full.tileset, 12, DMA); + VDP_setPalette(PAL1, font.palette -> data); + VDP_setTextPalette(1); + + XGM_setPCM(SFX_MENU_SELECT, sfxMenuSelect, sizeof(sfxMenuSelect)); // shit + XGM_setPCM(SFX_MENU_CHOOSE, sfxMenuChoose, sizeof(sfxMenuChoose)); + XGM_setPCM(SFX_START_GAME, sfxStartGame, sizeof(sfxStartGame)); + XGM_setPCM(SFX_PLAYER_SHOT, sfxPlayerShot, sizeof(sfxPlayerShot)); + XGM_setPCM(SFX_ZONE_OVER, sfxZoneOver, sizeof(sfxZoneOver)); + + XGM_setPCM(SFX_BULLET_1, sfxBullet1, sizeof(sfxBullet1)); + + XGM_setPCM(SFX_EXPLOSION_1, sfxExplosion1, sizeof(sfxExplosion1)); + XGM_setPCM(SFX_EXPLOSION_2, sfxExplosion2, sizeof(sfxExplosion2)); + XGM_setPCM(SFX_EXPLOSION_3, sfxExplosion3, sizeof(sfxExplosion3)); + + XGM_setPCM(SFX_GAME_OVER, sfxGameOver, sizeof(sfxGameOver)); + XGM_setPCM(SFX_BEAT_GAME, sfxBeatGame, sizeof(sfxBeatGame)); + +} + +void loadGame(){ + currentZone = 1; + started = TRUE; + zoneStarting = TRUE; + gameStarting = TRUE; + noMiss = TRUE; + yinBulletSpeed = 4; + podBulletSpeed = 3; + currentScore = 0; + loadExplosion(); + XGM_startPlay(&bgmStage1); +} + +void resetGame(){ + VDP_clearPlane(BG_A, TRUE); + VDP_clearPlane(BG_B, TRUE); + DMA_waitCompletion(); + startClock = 0; + zoneOver = FALSE; + started = FALSE; + gameOverClock = 0; + paused = FALSE; + gameOver = FALSE; + loadedChromeGameOver = FALSE; + loadedZoneOver = FALSE; + zoneStarting = FALSE; + gameClock = 0; + playerLives = 2; + playerBombs = 3; + bossType = 0; + noMiss = TRUE; + SPR_reset(); + for(s8 i = 0; i < BG_SCROLL_WIDTH; i++) backgroundScrolls[i] = 0; + VDP_setVerticalScrollTile(BG_B, 0, backgroundScrolls, BG_SCROLL_WIDTH, DMA_QUEUE); + VDP_setScreenWidth320(); + loadStart(); +} + + +void updateGame(){ + if(started && !paused){ + updateBackground(); + updateEnemies(); + updatePlayer(); + updateExplosion(); + gameClock++; + if(gameClock >= 1800) gameClock = 0; + } + updateChrome(); + if(zoneStarting) zoneStarting = FALSE; + if(gameStarting) gameStarting = FALSE; + if(doZoneStart){ + zoneStarting = TRUE; + doZoneStart = FALSE; + } + if(!gameOver && started && gameClock >= 15 && !zoneOver){ + if(controls.start && !pausing){ + pausing = TRUE; + paused = paused ? FALSE : TRUE; + } else if(!controls.start && pausing) pausing = FALSE; + } else if(gameOver){ + if(gameOverClock < 600) gameOverClock++; + if(gameOverClock >= 120 && (controls.a || controls.b || controls.c || controls.start)) resetGame(); + } +}; + +void nextZone(){ + if(currentZone == 21){ + resetGame(); + } else { + SPR_reset(); + loadExplosion(); + if(currentZone % 5 == 0) loadBoss(); + resetBackground(); + resetEnemies(); + resetPlayer(); + doZoneStart = TRUE; + zoneOver = FALSE; + loadedZoneOver = FALSE; + gameClock = -5; + noMiss = TRUE; + XGM_startPlayPCM(SFX_START_GAME, 1, SOUND_PCM_CH2); + if(currentZone == 11) XGM_startPlay(&bgmStage2); + } +} + +int main() { + XGM_setLoopNumber(-1); + JOY_init(); + JOY_setEventHandler(&updateControls); + loadResources(); + SPR_init(0, 0, 0); + loadStart(); + playerLives = 2; + playerBombs = 3; + while(1){ + started ? updateGame() : updateStart(); + SPR_update(); + SYS_doVBlankProcess(); + } + return (0); +} \ No newline at end of file diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..f32b980 --- /dev/null +++ b/src/main.h @@ -0,0 +1,29 @@ +#define GAME_WIDTH 256 +#define GAME_HEIGHT 224 + +#define SFX_MENU_SELECT 64 +#define SFX_MENU_CHOOSE 65 +#define SFX_START_GAME 66 +#define SFX_PLAYER_SHOT 67 +#define SFX_ZONE_OVER 68 + +#define SFX_BULLET_1 69 + +#define SFX_EXPLOSION_1 73 +#define SFX_EXPLOSION_2 74 +#define SFX_EXPLOSION_3 75 + +#define SFX_GAME_OVER 76 +#define SFX_BEAT_GAME 77 + +bool gameOver, noMiss, paused, pausing, zoneOver, zoneStarting, gameStarting, noMiss, doZoneStart; + +s16 gameClock, currentZone, gameOverClock; +s32 currentScore, highScore; + +void loadResources(), + loadGame(), + resetGame(), + updateGame(); + +int main(); \ No newline at end of file diff --git a/src/player.c b/src/player.c new file mode 100644 index 0000000..899f997 --- /dev/null +++ b/src/player.c @@ -0,0 +1,173 @@ +#include +#include +#include "player.h" +#include "main.h" +#include "enemies.h" +#include "controls.h" + + +// movement + +void updatePlayerBounds(){ + if(playerPos.x < PLAYER_LIMIT_LEFT) playerPos.x = PLAYER_LIMIT_LEFT; + else if(playerPos.x > PLAYER_LIMIT_RIGHT) playerPos.x = PLAYER_LIMIT_RIGHT; + if(playerPos.y < PLAYER_LIMIT_TOP) playerPos.y = PLAYER_LIMIT_TOP; + else if(playerPos.y > PLAYER_LIMIT_BOTTOM) playerPos.y = PLAYER_LIMIT_BOTTOM; +} + +void updatePlayerMove(){ + playerVelocity.x = FIX16(0); + playerVelocity.y = FIX16(0); + if(controls.left){ + if(controls.down){ + playerVelocity.x = MOVEMENT_NORMALIZE_NEG; + playerVelocity.y = MOVEMENT_NORMALIZE; + } + else if(controls.up){ + playerVelocity.x = MOVEMENT_NORMALIZE_NEG; + playerVelocity.y = MOVEMENT_NORMALIZE_NEG; + } + else playerVelocity.x = MOVEMENT_NEG; + } else if(controls.right){ + if(controls.down){ + playerVelocity.x = MOVEMENT_NORMALIZE; + playerVelocity.y = MOVEMENT_NORMALIZE; + } + else if(controls.up){ + playerVelocity.x = MOVEMENT_NORMALIZE; + playerVelocity.y = MOVEMENT_NORMALIZE_NEG; + } + else playerVelocity.x = MOVEMENT_POS; + } else if(controls.up || controls.down) playerVelocity.y = controls.up ? MOVEMENT_NEG : MOVEMENT_POS; + playerPos.x += fix16Mul(playerVelocity.x, controls.b ? PLAYER_SPEED_FOCUS : PLAYER_SPEED); + playerPos.y += fix16Mul(playerVelocity.y, controls.b ? PLAYER_SPEED_FOCUS : PLAYER_SPEED); + updatePlayerBounds(); + SPR_setPosition(playerSprite, fix16ToInt(playerPos.x) - 11, fix16ToInt(playerPos.y) - 16); +} + + +// shooting + +void spawnPlayerBullet(){ + static s16 i = 0; + for(s16 j = 0; j < PLAYER_BULLET_LIMIT; j++) if(!playerBullets[j].active) i = j; + playerBullets[i].active = TRUE; + playerBullets[i].pos.x = playerPos.x; + playerBullets[i].pos.y = fix16Sub(playerPos.y, FIX16(8)); + XGM_startPlayPCM(SFX_PLAYER_SHOT, 0, SOUND_PCM_CH4); +} + +void removePlayerBullet(s16 i){ + playerBullets[i].pos.x = FIX16(PLAYER_BULLET_DUMP_X); + playerBullets[i].pos.y = FIX16(PLAYER_BULLET_DUMP_Y); + SPR_setPosition(playerBullets[i].image, PLAYER_BULLET_DUMP_X, PLAYER_BULLET_DUMP_Y); + playerBullets[i].active = FALSE; +} + +void updatePlayerBullets(){ + for(s16 i = 0; i < PLAYER_BULLET_LIMIT; i++) if(playerBullets[i].active){ + playerBullets[i].pos.y = fix16Sub(playerBullets[i].pos.y, PLAYER_BULLET_SPEED); + SPR_setPosition(playerBullets[i].image, fix16ToInt(playerBullets[i].pos.x) - 8, fix16ToInt(playerBullets[i].pos.y) - 8); + if(playerBullets[i].pos.y <= PLAYER_BULLET_UP_LIMIT || playerBullets[i].pos.y >= PLAYER_BULLET_DOWN_LIMIT || zoneOver) removePlayerBullet(i); + } +} + +void updatePlayerShot(){ + if(playerShotClock >= PLAYER_SHOT_INTERVAL && controls.a) playerShotClock = 0; + if(playerShotClock == 0 && !zoneOver) spawnPlayerBullet(); + playerShotClock++; + if(playerShotClock >= 600) playerShotClock = PLAYER_SHOT_INTERVAL; +} + + +// bomb + +void spawnBomb(){ + killBullets = TRUE; + SND_startPlayPCM_XGM(random() % 2 < 1 ? SFX_EXPLOSION_1 : SFX_EXPLOSION_2, 15, SOUND_PCM_CH2); + spawnExplosion(random() % GAME_WIDTH, random() % GAME_HEIGHT, FALSE); +} + +void updatePlayerBomb(){ + if(bombing){ + if(bombClock % BOMB_INTERVAL == 0) spawnBomb(); + bombClock++; + if(bombClock >= BOMB_LIMIT){ + bombClock = 0; + bombing = FALSE; + } + } else if(controls.c && playerBombs > 0){ + bombing = TRUE; + playerBombs -= 1; + } +} + + +// get hit + +void updatePlayerHit(){ + if(hitPlayer){ + hitPlayer = FALSE; + playerRecovering = TRUE; + playerPos.x = PLAYER_INIT_X; + playerPos.y = PLAYER_INIT_Y; + playerLives -= 1; + noMiss = FALSE; + XGM_startPlayPCM(random() % 2 < 1 ? SFX_EXPLOSION_1 : SFX_EXPLOSION_2, 1, SOUND_PCM_CH4); + spawnExplosion(fix16ToInt(playerPos.x), fix16ToInt(playerPos.y), TRUE); + // if(playerLives < 0) playerLives = 0; + if(playerLives < 0) gameOver = TRUE; + } + if(!gameOver && playerRecovering){ + if(recoverClock % RECOVER_INTERVAL == 0) SPR_setVisibility(playerSprite, HIDDEN); + else if(recoverClock % RECOVER_INTERVAL == RECOVER_INTERVAL_HALF) SPR_setVisibility(playerSprite, VISIBLE); + recoverClock++; + if(recoverClock >= RECOVER_MAX){ + SPR_setVisibility(playerSprite, VISIBLE); + recoverClock = 0; + playerRecovering = FALSE; + } + } +} + + +// loop + + +void loadPlayer(){ + playerShotClock = PLAYER_SHOT_INTERVAL; + playerPos.x = PLAYER_INIT_X; + playerPos.y = PLAYER_INIT_Y; + playerSprite = SPR_addSprite(&imgPlayer, playerPos.x, playerPos.y, TILE_ATTR(PAL1, 1, FALSE, FALSE)); + SPR_setDepth(playerSprite, 4); + for(s16 i = 0; i < PLAYER_BULLET_LIMIT; i++){ + playerBullets[i].image = SPR_addSprite(&imgBullet, PLAYER_BULLET_DUMP_X, PLAYER_BULLET_DUMP_Y, TILE_ATTR(PAL1, 1, FALSE, FALSE)); + SPR_setDepth(playerBullets[i].image, 4); + } +} + +void resetPlayer(){ + playerPos.x = PLAYER_INIT_X; + playerPos.y = PLAYER_INIT_Y; + SPR_releaseSprite(playerSprite); + for(s16 i = 0; i < PLAYER_BULLET_LIMIT; i++) if(playerBullets[i].active){ + playerBullets[i].active = FALSE; + SPR_releaseSprite(playerBullets[i].image); + } +} + +void updatePlayer(){ + if(zoneStarting) loadPlayer(); + else { + updatePlayerBullets(); + if(!gameOver && !paused){ + if(zoneOver) SPR_setVisibility(playerSprite, HIDDEN); + else { + updatePlayerMove(); + updatePlayerHit(); + } + updatePlayerShot(); + updatePlayerBomb(); + } else if(gameOver) resetPlayer(); + } +} \ No newline at end of file diff --git a/src/player.h b/src/player.h new file mode 100644 index 0000000..5ab04e3 --- /dev/null +++ b/src/player.h @@ -0,0 +1,61 @@ +#define PLAYER_LIMIT_LEFT FIX16(4) +#define PLAYER_LIMIT_RIGHT fix16Sub(FIX16(GAME_WIDTH), PLAYER_LIMIT_LEFT) +#define PLAYER_LIMIT_TOP PLAYER_LIMIT_LEFT +#define PLAYER_LIMIT_BOTTOM fix16Sub(FIX16(GAME_HEIGHT), PLAYER_LIMIT_LEFT) + +#define MOVEMENT_NORMALIZE FIX16(0.707) +#define MOVEMENT_NORMALIZE_NEG FIX16(-0.707) +#define MOVEMENT_POS FIX16(1) +#define MOVEMENT_NEG FIX16(-1) + +#define PLAYER_SPEED FIX16(3.25) +#define PLAYER_SPEED_FOCUS FIX16(2) + +#define PLAYER_INIT_X FIX16(GAME_WIDTH / 2) +#define PLAYER_INIT_Y FIX16(GAME_HEIGHT - 32) + +#define PLAYER_BULLET_LIMIT 3 +#define PLAYER_SHOT_INTERVAL 10 +#define PLAYER_BULLET_SPEED FIX16(8 * 4) + +#define PLAYER_BULLET_UP_LIMIT FIX16(8) +#define PLAYER_BULLET_DOWN_LIMIT FIX16(GAME_HEIGHT + 16) + +#define PLAYER_BULLET_DUMP_X GAME_WIDTH + 16 +#define PLAYER_BULLET_DUMP_Y GAME_HEIGHT + 16 + +#define RECOVER_INTERVAL 30 +#define RECOVER_INTERVAL_HALF 15 +#define RECOVER_MAX RECOVER_INTERVAL * 6 + +#define BOMB_LIMIT 200 +#define BOMB_INTERVAL 10 + +struct playerBullet { + bool active, downward; + Vect2D_f16 pos; + Sprite* image; +}; +struct playerBullet playerBullets[PLAYER_BULLET_LIMIT]; + +Sprite* playerSprite; + +Vect2D_f16 playerPos, playerVelocity; + +s16 playerShotClock, recoverClock, bombClock; +s8 playerLives, playerBombs; + +bool hitPlayer, playerRecovering, bombing; + +void loadPlayer(), + updatePlayer(), + updatePlayerBounds(), + updatePlayerMove(), + updatePlayerShot(), + updatePlayerBullets(), + spawnPlayerBullet(), + spawnBomb(), + updatePlayerBomb(), + resetPlayer(), + updatePlayerHit(), + removePlayerBullet(s16); \ No newline at end of file diff --git a/src/pod.c b/src/pod.c new file mode 100644 index 0000000..70108c0 --- /dev/null +++ b/src/pod.c @@ -0,0 +1,125 @@ +#include +#include + +#include "main.h" +#include "enemies.h" +#include "pod.h" +#include "explosion.h" +#include "boss.h" + + +// spawn + +void spawnPod(struct podSpawner spawner){ + s16 i = -1; + for(s16 h = 0; h < POD_COUNT; h++) if(!pods[h].active && i == -1) i = h; + if(i > -1){ + pods[i].active = TRUE; + pods[i].pos.x = spawner.x; + pods[i].pos.y = spawner.y; + pods[i].clock = 0; + pods[i].random = spawner.random; + pods[i].image = SPR_addSprite(&imgSpider, fix16ToInt(POD_DUMP_X), fix16ToInt(POD_DUMP_Y), TILE_ATTR(PAL1, 0, FALSE, FALSE)); + } +} + + +// shoot + +void podPatternOne(s16 i){ + struct bulletSpawner bSpawn = { + .x = pods[i].pos.x, + .y = pods[i].pos.y, + .type = 4, + .angle = random() % 1024, + .speed = FIX16(podBulletSpeed) + }; + for(s8 b = 0; b < 3; b++){ + spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += 341; + } + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); +} + +void podPatternTwo(s8 i, s8 mod, s16 aMod){ + struct bulletSpawner bSpawn = { + .x = pods[i].pos.x, + .y = pods[i].pos.y, + .type = 4, + .angle = random() % 1024, + .speed = FIX16(podBulletSpeed) + }; + for(s8 b = 0; b < 3 + mod; b++){ + spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += aMod; + } + bSpawn.speed = FIX16(podBulletSpeed + 1); + bSpawn.type = 3; + bSpawn.angle += aMod / 2; + for(s8 b = 0; b < 3 + mod; b++){ + spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.angle += aMod; + } +} + +void shootPod(s16 i){ + if(currentZone == 1) podPatternOne(i); + else if(currentZone < 5) podPatternTwo(i, 0, 342); + else if(currentZone < 10) podPatternTwo(i, 1, 256); + else podPatternTwo(i, 2, 204); +} + + +// die + +void killPod(s16 i){ + pods[i].active = FALSE; + pods[i].pos.x = POD_DUMP_X; + pods[i].pos.y = POD_DUMP_Y; + SPR_releaseSprite(pods[i].image); +} + + +// loop + +void loadPod(){ + if(currentZone % 5 == 1 && currentZone > 1) podBulletSpeed++; + currentPodCount = 0; + if(currentZone >= 3) currentPodCount = 1; + if(currentZone >= 6) currentPodCount = 2; + if(currentZone >= 11) currentPodCount = 3; + if(currentZone >= 16) currentPodCount = 4; + if(currentPodCount > 0){ + for(s16 i = 0; i < POD_COUNT; i++){ + pods[i].pos.x = POD_DUMP_X; + pods[i].pos.y = POD_DUMP_Y; + pods[i].random = FALSE; + pods[i].clock = 0; + } + } +} + +void resetPod(){ + for(s16 i = 0; i < POD_COUNT; i++){ + pods[i].active = FALSE; + pods[i].pos.x = POD_DUMP_X; + pods[i].pos.y = POD_DUMP_Y; + SPR_releaseSprite(pods[i].image); + } +} + +void updatePod(){ + for(s16 i = 0; i < POD_COUNT; i++) if(pods[i].active) { + if(pods[i].clock >= POD_TIME_LIMIT){ + if(!gameOver) shootPod(i); + killPod(i); + spawnExplosion(fix16ToInt(pods[i].pos.x), fix16ToInt(pods[i].pos.y), FALSE); + XGM_startPlayPCM(SFX_BULLET_1, 1, SOUND_PCM_CH3); + } else { + if(pods[i].clock % 60 == 0 && pods[i].clock > 0) SPR_setAnimAndFrame(pods[i].image, pods[i].clock / 60, pods[i].clock % 40 == 0 ? 0 : 1); + SPR_setPosition(pods[i].image, fix16ToInt(pods[i].pos.x) - 8, fix16ToInt(pods[i].pos.y) - 8); + } + if(zoneOver) killPod(i); + pods[i].clock++; + } +} \ No newline at end of file diff --git a/src/pod.h b/src/pod.h new file mode 100644 index 0000000..270b7e0 --- /dev/null +++ b/src/pod.h @@ -0,0 +1,35 @@ +#define POD_COUNT 16 + +#define POD_DUMP_X FIX16(GAME_WIDTH + 32) +#define POD_DUMP_Y FIX16(GAME_HEIGHT + 32) + +#define POD_TIME_LIMIT 150 + +struct pod { + bool active, random; + s16 clock; + Vect2D_f16 pos; + Sprite* image; +}; + +struct podSpawner { + f16 x, y; + bool random; +}; + +struct pod pods[POD_COUNT]; + +s16 currentPodCount; + +Vect2D_f16 currentPodPos[POD_COUNT]; + +bool foundRandomPodMatch; + +void loadPod(), + podPatternOne(s16), + podPatternTwo(s8, s8, s16), + shootPod(s16), + destroyPod(s16), + killPod(s16), + resetPod(), + updatePod(); \ No newline at end of file diff --git a/src/start.c b/src/start.c new file mode 100644 index 0000000..b54766e --- /dev/null +++ b/src/start.c @@ -0,0 +1,245 @@ +#include +#include + +#include "main.h" +#include "controls.h" +#include "player.h" +#include "start.h" + + +// background + +void loadStartBg(){ + for(s8 y = 0; y < START_BG_HEIGHT; y++) + for(s8 x = -1; x < START_BG_WIDTH; x++) if(y > START_BG_HEIGHT - 2) VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL1, 0, 0, 0, 14), x, y); + loadStartGradient(); +} + +void loadStartGradient(){ + for(s8 x = 0; x < START_BG_WIDTH; x++){ + VDP_drawImageEx(BG_B, &startGradient, TILE_ATTR_FULL(PAL1, 0, 0, 0, 200), x, START_GRADIENT_Y, 0, DMA_QUEUE); + for(s8 y = 0; y < 12; y++) VDP_setTileMapXY(BG_A, TILE_ATTR_FULL(PAL1, 0, 0, 0, 11), x, y + START_GRADIENT_Y); + } +} + + +// logo + +void loadStartLogo(){ + s8 logoCount = 0; + for(s8 i = 0; i < START_LOGO_LINES; i++) startLogoScrolls[i] = random() % 4 - 2; + VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_2TILE); + VDP_drawImageEx(BG_B, &startLogo, TILE_ATTR_FULL(PAL1, 0, 0, 0, 15), 3, START_LOGO_Y, 0, DMA_QUEUE); + VDP_drawImageEx(BG_B, &startLogoV, TILE_ATTR_FULL(PAL1, 0, 0, 0, 100), 3, START_LOGO_Y + 4, 0, DMA_QUEUE); + animateStartLogo(); +} + +void animateStartLogo(){ + if(startClock % 8 == 0){ + for(s8 i = 0; i < START_LOGO_LINES; i++){ + if(aboutShowing){ + startLogoScrolls[i] = 0; + } else { + if(i < START_LOGO_LINES / 3 || (i >= START_LOGO_LINES / 3 && i < START_LOGO_LINES / 3 * 2 && startClock % 16 == 0) || + (i >= START_LOGO_LINES / 3 * 2 && startClock % 32 == 0)){ + startLogoScrollsFlip[i] ? startLogoScrolls[i]-- : startLogoScrolls[i]++; + if(startLogoScrolls[i] >= (2)) startLogoScrollsFlip[i] = TRUE; + else if(startLogoScrolls[i] <= ( -2)) startLogoScrollsFlip[i] = FALSE; + } + } + } + VDP_setHorizontalScrollLine(BG_B, 72, startLogoScrolls, START_LOGO_LINES, DMA_QUEUE); + } +} + + +// menu + +void loadStartMenu(){ + VDP_drawText(">", START_MENU_X - 1, START_MENU_Y); + VDP_drawText("START GAME", START_MENU_X, START_MENU_Y); + VDP_drawText("ABOUT", START_MENU_X, START_MENU_Y + 2); + VDP_drawText("LIVES", START_MENU_X, START_MENU_Y + 4); + VDP_drawText("BOMBS", START_MENU_X, START_MENU_Y + 6); +} + +void updateStartLives(){ + if(currentStartMenu == 2 && (controls.left || controls.right) && !updatingStartCount){ + if(controls.right && playerLives < 6) playerLives++; + else if(controls.left && playerLives > 0) playerLives--; + updatingStartCount = TRUE; + } else if(!controls.left && !controls.right && updatingStartCount) updatingStartCount = FALSE; + if(startCurrentLives != playerLives){ + startCurrentLives = playerLives; + VDP_clearTileMapRect(BG_A, START_MENU_X + 6, START_MENU_Y + 4, 6, 1); + for(s8 x = 0; x < playerLives; x++) VDP_drawText("#", x + START_MENU_X + 6, START_MENU_Y + 4); + } +} + +void updateStartBombs(){ + if(currentStartMenu == 3 && (controls.left || controls.right) && !updatingStartCount){ + if(controls.right && playerBombs < 6) playerBombs++; + else if(controls.left && playerBombs > 0) playerBombs--; + updatingStartCount = TRUE; + } else if(!controls.left && !controls.right && updatingStartCount) updatingStartCount = FALSE; + if(startCurrentBombs != playerBombs){ + startCurrentBombs = playerBombs; + VDP_clearTileMapRect(BG_A, START_MENU_X + 6, START_MENU_Y + 6, 6, 1); + for(s8 x = 0; x < playerBombs; x++) VDP_drawText("*", x + START_MENU_X + 6, START_MENU_Y + 6); + } +} + +void updateStartMenu(){ + if(currentStartMenu != lastStartMenu && !aboutShowing){ + VDP_clearTileMapRect(BG_A, START_MENU_X - 1, START_MENU_Y + lastStartMenu * 2, 1, 1); + VDP_drawText(">", START_MENU_X - 1, START_MENU_Y + currentStartMenu * 2); + lastStartMenu = currentStartMenu; + } + if((controls.up || controls.down) && !selectingStartMenu && !aboutShowing){ + currentStartMenu += controls.up ? -1 : 1; + if(currentStartMenu > 3) currentStartMenu = 0; + else if(currentStartMenu < 0) currentStartMenu = 3; + selectingStartMenu = TRUE; + XGM_startPlayPCM(SFX_MENU_SELECT, 1, SOUND_PCM_CH2); + } else if(!controls.up && !controls.down && !controls.a && !controls.b && !controls.c && !controls.start && selectingStartMenu) selectingStartMenu = FALSE; + updateStartLives(); + updateStartBombs(); +} + +void selectStartMenu(){ + selectingStartMenu = TRUE; + switch(currentStartMenu){ + case 0: + resetStart(); + XGM_startPlayPCM(SFX_START_GAME, 1, SOUND_PCM_CH2); + loadGame(); + break; + case 1: + loadStartAbout(); + XGM_startPlayPCM(SFX_MENU_CHOOSE, 1, SOUND_PCM_CH2); + break; + } +} + + +// about + +void loadStartAbout(){ + aboutShowing = TRUE; + for(s8 x = 0; x < START_BG_WIDTH; x++) + for(s8 y = 0; y < START_BG_HEIGHT; y++) + VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL1, 0, 0, 0, 13), x, y); + VDP_clearTileMapRect(BG_A, 0, 0, START_BG_WIDTH, START_BG_HEIGHT); + aboutY = 2; + VDP_drawText("the story so far", 8, aboutY); + aboutY += 2; + VDP_drawText("MOMOYO IS YOUR REGULAR HARD-", 2, aboutY); aboutY++; + VDP_drawText("WORKING CENTIPEDE WHO LIVES IN", 1, aboutY); aboutY++; + VDP_drawText("THE MOMENT... BUT DOES HER NEW", 1, aboutY); aboutY++; + VDP_drawText("BOSS HAVE HER MAKING NEW PLANS", 1, aboutY); aboutY += 2; + VDP_drawText("USE HER TOOLS OVER 20 STAGES &", 1, aboutY); aboutY++; + VDP_drawText("DIG UP SOME SWEETS TO BRING TO", 1, aboutY); aboutY++; + VDP_drawText("##MEGUMU##", 11, aboutY); + aboutY += 2; + VDP_drawText("how to play", 1, aboutY); aboutY += 2; + VDP_drawText("d=pad MOVE", 1, aboutY); aboutY++; + VDP_drawText("a SHOOT", 1, aboutY); aboutY++; + VDP_drawText("b FOCUS/SLOW", 1, aboutY); aboutY++; + VDP_drawText("c BOMB", 1, aboutY); aboutY++; + VDP_drawText("start PAUSE", 1, aboutY); + aboutY += 3; + VDP_drawText("EVERYTHING BY T.BODDY", 1, aboutY); aboutY++; + VDP_drawText("FOR touhou pride game jam iii", 1, aboutY); aboutY++; + VDP_drawText("TOUHOU PROJECT COPYRIGHT ZUN?", 1, aboutY); aboutY += 2; + VDP_drawText("press any button to go back", 1, aboutY); aboutY++; + startClock = SEGA_LIMIT + 120; +} + +void startGoBack(){ + selectingStartMenu = TRUE; + aboutShowing = FALSE; + currentStartMenu = 0; + VDP_clearTileMapRect(BG_B, 0, 0, START_BG_WIDTH, START_BG_HEIGHT); + VDP_clearTileMapRect(BG_A, 0, 0, START_BG_WIDTH, START_BG_HEIGHT); + startCurrentBombs = -1; + startCurrentLives = -1; + for(s8 y = 0; y < START_BG_HEIGHT; y++) + for(s8 x = -1; x < START_BG_WIDTH; x++) VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL1, 0, 0, 0, 13), x, y); + loadStartBg(); + loadStartLogo(); + loadStartMenu(); + loadStartScore(); + loadStartCredit(); +} + +void updateStartAbout(){ + if(startClock % 120 == 0) VDP_drawImageEx(BG_B, &startAbout, TILE_ATTR_FULL(PAL1, 0, 0, 0, 64), 22, 12, 0, DMA_QUEUE); + else if(startClock % 120 == 60) VDP_drawImageEx(BG_B, &startAbout2, TILE_ATTR_FULL(PAL1, 0, 0, 0, 128), 22, 12, 0, DMA_QUEUE); +} + + +// score & credit + +void loadStartScore(){ + intToStr(highScore, startHighScoreStr, 8); + VDP_drawText("HI", 1, 26); + VDP_drawText(startHighScoreStr, 4, 26); +} + +void loadStartCredit(){ + VDP_drawText("06.2021 T.BODDY", 16, 26); +} + + +// loop + +void loadStart(){ + VDP_loadTileSet(startTop.tileset, 13, DMA); + VDP_loadTileSet(startBottom.tileset, 14, DMA); + for(s8 y = 0; y < START_BG_HEIGHT; y++) + for(s8 x = -1; x < START_BG_WIDTH; x++) VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL1, 0, 0, 0, 13), x, y); + segaImage1 = SPR_addSprite(&startBoddy1, SEGA_X - 56, SEGA_Y, TILE_ATTR(PAL1, 0, 0, 0)); + segaImage2 = SPR_addSprite(&startBoddy2, SEGA_X, SEGA_Y, TILE_ATTR(PAL1, 0, 0, 0)); + // VDP_drawImage(BG_B, &startBoddy, 0, 0); +} + +void resetStart(){ + VDP_clearTileMapRect(BG_B, 0, 0, START_BG_WIDTH, START_BG_HEIGHT); + VDP_clearTileMapRect(BG_A, 0, 0, START_BG_WIDTH, START_BG_HEIGHT); +} + +void updateStart(){ + if(startClock >= SEGA_LIMIT + 15){ + updateStartMenu(); + animateStartLogo(); + if((controls.a || controls.b || controls.c || controls.start) && aboutShowing && !selectingStartMenu) startGoBack(); + else if((controls.a || controls.start) && !aboutShowing && !selectingStartMenu) selectStartMenu(); + if(aboutShowing) updateStartAbout(); + } else if(startClock == SEGA_LIMIT - 25){ + for(s8 y = 0; y < START_BG_HEIGHT; y++) + for(s8 x = -1; x < START_BG_WIDTH; x++) VDP_setTileMapXY(BG_A, TILE_ATTR_FULL(PAL1, 1, 0, 0, 10), x, y); + } else if(startClock == SEGA_LIMIT - 20){ + for(s8 y = 0; y < START_BG_HEIGHT; y++) + for(s8 x = -1; x < START_BG_WIDTH; x++) VDP_setTileMapXY(BG_A, TILE_ATTR_FULL(PAL1, 1, 0, 0, 11), x, y); + } else if(startClock == SEGA_LIMIT - 15){ + SPR_releaseSprite(segaImage1); + SPR_releaseSprite(segaImage2); + } else if(startClock == SEGA_LIMIT - 10){ + VDP_setScreenWidth256(); + } else if(startClock == SEGA_LIMIT - 5){ + loadStartBg(); + } else if(startClock == SEGA_LIMIT){ + for(s8 y = 0; y < START_BG_HEIGHT; y++) + for(s8 x = -1; x < START_BG_WIDTH; x++) VDP_setTileMapXY(BG_A, TILE_ATTR_FULL(PAL1, 1, 0, 0, 10), x, y); + } else if(startClock == SEGA_LIMIT + 5){ + VDP_clearTileMapRect(BG_A, -1, 0, START_BG_WIDTH, START_BG_HEIGHT); + } else if(startClock == SEGA_LIMIT + 10){ + loadStartLogo(); + loadStartMenu(); + loadStartScore(); + loadStartCredit(); + XGM_startPlay(&bgmStart); + } + startClock++; + if(startClock >= 1800) startClock = SEGA_LIMIT + 120; +} \ No newline at end of file diff --git a/src/start.h b/src/start.h new file mode 100644 index 0000000..95eca93 --- /dev/null +++ b/src/start.h @@ -0,0 +1,48 @@ +#define START_BG_HEIGHT 28 +#define START_BG_WIDTH 40 + +#define START_MENU_X 11 +#define START_MENU_Y 15 + +#define START_LOGO_LINES 32 +#define START_LOGO_Y 5 + +#define START_GRADIENT_Y 23 + +// #define SEGA_LIMIT 90 +#define SEGA_LIMIT 60 * 4 + +#define SEGA_X 320 / 2 +#define SEGA_Y GAME_HEIGHT / 2 - 16 + +bool started, selectingStartMenu, aboutShowing, loadedStart; + +s16 currentStartMenu, lastStartMenu, startClock, aboutY; + +s16 startLogoScrolls[START_LOGO_LINES]; +bool startLogoScrollsFlip[START_LOGO_LINES], updatingStartCount; + +s8 startCurrentLives, startCurrentBombs; + +char startHighScoreStr[10]; + +Sprite* segaImage1; +Sprite* segaImage2; + +void loadStartBg(), + loadStart(), + loadStartLogo(), + loadStartMenu(), + loadStartGradient(), + resetStart(), + animateStartLogo(), + startGoBack(), + selectStartMenu(), + updateStartMenu(), + loadStartScore(), + updateStartLives(), + updateStartBombs(), + updateStartAbout(), + loadStartCredits(), + loadStartAbout(), + updateStart(); \ No newline at end of file diff --git a/src/yin.c b/src/yin.c new file mode 100644 index 0000000..bc58623 --- /dev/null +++ b/src/yin.c @@ -0,0 +1,132 @@ +#include +#include + +#include "main.h" +#include "explosion.h" +#include "enemies.h" +#include "yin.h" + + +// movement + +void moveYinHorizontal(s8 i){ + yins[i].pos.x += yins[i].flag1 ? 0 - YIN_SPEED : YIN_SPEED; + if(yins[i].pos.x >= GAME_WIDTH - 10){ + yins[i].flag1 = TRUE; + SPR_setHFlip(yins[i].image, 1); + } else if(yins[i].pos.x <= 10){ + yins[i].flag1 = FALSE; + SPR_setHFlip(yins[i].image, 0); + } +} + +void moveYinVertical(s8 i){ + yins[i].pos.y += yins[i].flag1 ? 0 - YIN_SPEED : YIN_SPEED; + if(yins[i].pos.y >= 112){ + yins[i].flag1 = TRUE; + } else if(yins[i].pos.y <= 56){ + yins[i].flag1 = FALSE; + } +} + + +// shooting + +void yinPatternOne(s8 i){ + if(yins[i].clock % 80 == 40){ + struct bulletSpawner bSpawn = { + .x = FIX16(yins[i].pos.x), + .y = FIX16(yins[i].pos.y), + .type = 1 + }; + bSpawn.velocityX = honeEnemyBullet(bSpawn.x, bSpawn.y, yinBulletSpeed, 0, TRUE); + bSpawn.velocityY = honeEnemyBullet(bSpawn.x, bSpawn.y, yinBulletSpeed, 0, FALSE); + spawnEnemyBullet(bSpawn, eUpdate); + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + spawnExplosion(yins[i].pos.x, yins[i].pos.y, FALSE); + } +} + +void yinPatternTwo(s8 i){ + if(yins[i].clock % 40 == 20){ + struct bulletSpawner bSpawn = { + .x = FIX16(yins[i].pos.x), + .y = FIX16(yins[i].pos.y), + .type = yins[i].clock % 80 == 20 ? 2 : 1 + }; + bSpawn.velocityX = honeEnemyBullet(bSpawn.x, bSpawn.y, yinBulletSpeed, bSpawn.type == 1 ? 32 : 0, TRUE); + bSpawn.velocityY = honeEnemyBullet(bSpawn.x, bSpawn.y, yinBulletSpeed, bSpawn.type == 1 ? 32 : 0, FALSE); + spawnEnemyBullet(bSpawn, eUpdate); + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + spawnExplosion(yins[i].pos.x, yins[i].pos.y, FALSE); + } +} + +void yinPatternThree(s8 i, s8 count){ + if(yins[i].clock % 80 >= 20 && yins[i].clock % 80 < 30 && yins[i].clock % 5 == 0){ + struct bulletSpawner bSpawn = { + .x = FIX16(yins[i].pos.x), + .y = FIX16(yins[i].pos.y), + .type = yins[i].clock % 8 == 0 ? 2 : 1, + .int1 = 64 + }; + for(s8 b = 0; b < count; b++){ + bSpawn.velocityX = honeEnemyBullet(bSpawn.x, bSpawn.y, yinBulletSpeed, yins[i].horizontal ? bSpawn.int1 : 0, TRUE); + bSpawn.velocityY = honeEnemyBullet(bSpawn.x, bSpawn.y, yinBulletSpeed, yins[i].horizontal ? 0 : bSpawn.int1, FALSE); + spawnEnemyBullet(bSpawn, eUpdate); + bSpawn.type = bSpawn.type == 2 ? 1 : 2; + bSpawn.int1 += 16; + } + XGM_startPlayPCM(SFX_BULLET_1, 0, SOUND_PCM_CH3); + } +} + +void yinShoot(s8 i){ + if((!yins[i].horizontal && !yins[i].last && yins[i].clock % 240 < 80) || + (yins[i].horizontal && yins[i].clock % 240 >= 80 && yins[i].clock % 240 < 160) || + (yins[i].last && yins[i].clock % 240 >= 160)){ + if(currentZone == 1) yinPatternOne(i); + else if(currentZone < 5) yinPatternTwo(i); + else if(currentZone < 10) yinPatternThree(i, 1); + else if(currentZone < 15) yinPatternThree(i, 2); + else yinPatternThree(i, 3); + } +} + + +// loop + +void loadYins(){ + if(currentZone % 5 == 1 && currentZone > 1) yinBulletSpeed++; + for(s16 i = 0; i < YIN_COUNT; i++){ + yins[i].pos.x = i == 0 ? 6 : (i == 2 ? 251 : 128); + yins[i].pos.y = i % 2 == 0 ? (i == 0 ? 64 : 96) : 21; + yins[i].speed = 20; + yins[i].horizontal = i % 2 == 1; + yins[i].last = i % 3 == 2; + yins[i].clock = 0; + yins[i].image = SPR_addSprite(&imgYin1, yins[i].pos.x, yins[i].pos.y, TILE_ATTR(PAL1, 0, FALSE, FALSE)); + if(yins[i].last) yins[i].flag1 = TRUE; + SPR_setDepth(yins[i].image, 1); + } +} + +void resetYins(){ + for(s16 i = 0; i < YIN_COUNT; i++){ + yins[i].pos.x = i == 0 ? 6 : (i == 2 ? 251 : 128); + yins[i].pos.y = i % 2 == 0 ? (i == 0 ? 64 : 96) : 21; + SPR_releaseSprite(yins[i].image); + } +} + +void updateYins(){ + for(s16 i = 0; i < YIN_COUNT; i++){ + if(yins[i].clock % yins[i].speed == 0) yins[i].horizontal ? moveYinHorizontal(i) : moveYinVertical(i); + else if(yins[i].clock % yins[i].speed == 1) SPR_setPosition(yins[i].image, yins[i].pos.x - 4, yins[i].pos.y - 4); + if(yins[i].clock % 5 == 0 && yins[i].speed > 2) yins[i].speed--; + if(zoneOver) SPR_setVisibility(yins[i].image, HIDDEN); + else if(!gameOver) yinShoot(i); + if(!zoneOver) yins[i].clock++; + if(yins[i].clock >= 240) yins[i].clock = 0; + } +} \ No newline at end of file diff --git a/src/yin.h b/src/yin.h new file mode 100644 index 0000000..a41e21c --- /dev/null +++ b/src/yin.h @@ -0,0 +1,23 @@ +#define YIN_COUNT 3 +#define YIN_SPEED 2 + +struct yin { + Sprite* image; + Vect2D_s16 pos; + bool horizontal, last, flag1, flag2; + s16 clock, speed, int1, int2, int3; +}; + +struct yin yins[YIN_COUNT]; + +void loadYins(), + moveYinHorizontal(s8), + moveYinVertical(s8), + yinPatternOne(s8), + yinPatternTwo(s8), + yinPatternThree(s8, s8), + yinPatternFour(s8), + yinPatternFive(s8), + yinShoot(s8), + resetYins(), + updateYins(); \ No newline at end of file diff --git a/start.dmf b/start.dmf new file mode 100644 index 0000000..d759be0 Binary files /dev/null and b/start.dmf differ diff --git a/track1.dmf b/track1.dmf new file mode 100644 index 0000000..02c8f7f Binary files /dev/null and b/track1.dmf differ diff --git a/track2.dmf b/track2.dmf new file mode 100644 index 0000000..ba57916 Binary files /dev/null and b/track2.dmf differ