init
This commit is contained in:
parent
ab73e04b32
commit
0b905f2690
26 changed files with 1210 additions and 0 deletions
27
src/background.h
Normal file
27
src/background.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#define BG_I 8
|
||||
#define FADE_TOP_I BG_I + 64
|
||||
#define FADE_BOTTOM_I FADE_TOP_I + 64
|
||||
|
||||
#define BG_COUNT 27
|
||||
#define BG_OFF 112
|
||||
s16 bgScrolls[BG_COUNT];
|
||||
|
||||
void loadBackground(){
|
||||
VDP_loadTileSet(sky.tileset, BG_I, DMA);
|
||||
VDP_loadTileSet(fadeTop.tileset, FADE_TOP_I, DMA);
|
||||
VDP_loadTileSet(fadeBottom.tileset, FADE_BOTTOM_I, DMA);
|
||||
for(u8 y = 0; y < 4; y++){
|
||||
for(u8 x = 0; x < 16; x++){
|
||||
VDP_fillTileMapRectInc(BG_B, TILE_ATTR_FULL(PAL1, 0, 0, 0, BG_I), x * 8, y * 8, 8, 8);
|
||||
}
|
||||
}
|
||||
// for(u8 x = 0; x < 5; x++){
|
||||
// VDP_fillTileMapRectInc(BG_A, TILE_ATTR_FULL(PAL1, 0, 0, 0, FADE_TOP_I), x * 8, 0, 8, 8);
|
||||
// VDP_fillTileMapRectInc(BG_A, TILE_ATTR_FULL(PAL1, 0, 0, 0, FADE_BOTTOM_I), x * 8, 20, 8, 8);
|
||||
// }
|
||||
}
|
||||
|
||||
void updateBackground(){
|
||||
VDP_setHorizontalScroll(BG_B, fix32ToInt(-player.camera));
|
||||
VDP_setVerticalScroll(BG_B, (fix32ToInt(player.pos.y) - BG_OFF) >> 2);
|
||||
}
|
||||
33
src/boot/rom_head.c
Normal file
33
src/boot/rom_head.c
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#include "genesis.h"
|
||||
|
||||
__attribute__((externally_visible))
|
||||
const ROMHeader rom_header = {
|
||||
#if (ENABLE_BANK_SWITCH != 0)
|
||||
"SEGA SSF ",
|
||||
#elif (MODULE_MEGAWIFI != 0)
|
||||
"SEGA MEGAWIFI ",
|
||||
#else
|
||||
"SEGA MEGA DRIVE ",
|
||||
#endif
|
||||
"(C)SGDK 2024 ",
|
||||
"SAMPLE PROGRAM ",
|
||||
"SAMPLE PROGRAM ",
|
||||
"GM 00000000-00",
|
||||
0x000,
|
||||
"JD ",
|
||||
0x00000000,
|
||||
#if (ENABLE_BANK_SWITCH != 0)
|
||||
0x003FFFFF,
|
||||
#else
|
||||
0x000FFFFF,
|
||||
#endif
|
||||
0xE0FF0000,
|
||||
0xE0FFFFFF,
|
||||
"RA",
|
||||
0xF820,
|
||||
0x00200000,
|
||||
0x0020FFFF,
|
||||
" ",
|
||||
"DEMONSTRATION PROGRAM ",
|
||||
"JUE "
|
||||
};
|
||||
487
src/boot/sega.s
Normal file
487
src/boot/sega.s
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
#include "task_cst.h"
|
||||
|
||||
.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 rom_header
|
||||
|
||||
.org 0x00000000
|
||||
|
||||
_Start_Of_Rom:
|
||||
_Vecteurs_68K:
|
||||
dc.l __stack /* 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
|
||||
dc.l _INT
|
||||
dc.l _EXTINT
|
||||
dc.l _INT
|
||||
dc.l hintCaller
|
||||
dc.l _INT
|
||||
dc.l _VINT
|
||||
dc.l _INT
|
||||
dc.l _trap_0 /* Resume supervisor task */
|
||||
dc.l _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
|
||||
|
||||
rom_header:
|
||||
.incbin "out/rom_head.bin", 0, 0x100
|
||||
|
||||
_Entry_Point:
|
||||
* disable interrupts
|
||||
move #0x2700,%sr
|
||||
|
||||
* Configure a USER_STACK_LENGTH bytes user stack at bottom, and system stack on top of it
|
||||
move %sp, %usp
|
||||
sub #USER_STACK_LENGTH, %sp
|
||||
|
||||
* Halt Z80 (need to be done as soon as possible on reset)
|
||||
move.l #0xA11100,%a0 /* Z80_HALT_PORT */
|
||||
move.w #0x0100,%d0
|
||||
move.w %d0,(%a0) /* HALT Z80 */
|
||||
move.w %d0,0x0100(%a0) /* END RESET Z80 */
|
||||
|
||||
tst.l 0xa10008
|
||||
bne.s SkipInit
|
||||
|
||||
tst.w 0xa1000c
|
||||
bne.s SkipInit
|
||||
|
||||
* Check Version Number
|
||||
move.b -0x10ff(%a0),%d0
|
||||
andi.b #0x0f,%d0
|
||||
beq.s NoTMSS
|
||||
|
||||
* Sega Security Code (SEGA)
|
||||
move.l #0x53454741,0x2f00(%a0)
|
||||
|
||||
NoTMSS:
|
||||
jmp _start_entry
|
||||
|
||||
SkipInit:
|
||||
jmp _reset_entry
|
||||
|
||||
|
||||
*------------------------------------------------
|
||||
*
|
||||
* 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
|
||||
|
||||
_VINT:
|
||||
btst #5, (%sp) /* Skip context switch if not in user task */
|
||||
bne.s no_user_task
|
||||
|
||||
tst.w task_lock
|
||||
bne.s 1f
|
||||
move.w #0, -(%sp) /* TSK_superPend() will return 0 */
|
||||
bra.s unlock /* If lock == 0, supervisor task is not locked */
|
||||
|
||||
1:
|
||||
bcs.s no_user_task /* If lock < 0, super is locked with infinite wait */
|
||||
subq.w #1, task_lock /* Locked with wait, subtract 1 to the frame count */
|
||||
bne.s no_user_task /* And do not unlock if we did not reach 0 */
|
||||
move.w #1, -(%sp) /* TSK_superPend() will return 1 */
|
||||
|
||||
unlock:
|
||||
/* Save bg task registers (excepting a7, that is stored in usp) */
|
||||
move.l %a0, task_regs
|
||||
lea (task_regs + UTSK_REGS_LEN), %a0
|
||||
movem.l %d0-%d7/%a1-%a6, -(%a0)
|
||||
|
||||
move.w (%sp)+, %d0 /* Load return value previously pushed to stack */
|
||||
|
||||
move.w (%sp)+, task_sr /* Pop user task sr and pc, and save them, */
|
||||
move.l (%sp)+, task_pc /* so they can be restored later. */
|
||||
movem.l (%sp)+, %d2-%d7/%a2-%a6 /* Restore non clobberable registers */
|
||||
|
||||
no_user_task:
|
||||
/* At this point, we always have in the stack the SR and PC of the task */
|
||||
/* we want to jump after processing the interrupt, that might be the */
|
||||
/* point where we came from (if there is no context switch) or the */
|
||||
/* supervisor task (if we unlocked it). */
|
||||
|
||||
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
|
||||
* A<B, so ret 0, rem A
|
||||
move.l %d0,%d1
|
||||
clr.l %d0
|
||||
move.l %a2,%d3 /* restore d3 */
|
||||
rts
|
||||
* A==B, so ret 1, rem 0
|
||||
is1:
|
||||
moveq.l #1,%d0
|
||||
clr.l %d1
|
||||
move.l %a2,%d3 /* restore d3 */
|
||||
rts
|
||||
* A>B 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
|
||||
171
src/bullets.h
Normal file
171
src/bullets.h
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
#define BULLET_OFF 8
|
||||
#define P_BULLET_OFF 16
|
||||
|
||||
static void doBulletRotation(u8 i){
|
||||
if(bullets[i].anim >= FIRST_ROTATING_BULLET && !bullets[i].player){
|
||||
bullets[i].vFlip = FALSE;
|
||||
bullets[i].hFlip = FALSE;
|
||||
|
||||
if(bullets[i].angle < 0) bullets[i].angle += 1024;
|
||||
else if(bullets[i].angle >= 1024) bullets[i].angle -= 1024;
|
||||
|
||||
|
||||
// 0 - 256
|
||||
if(bullets[i].angle >= 1008 || bullets[i].angle < 16) bullets[i].frame = 0;
|
||||
else if(bullets[i].angle >= 16 && bullets[i].angle < 48) bullets[i].frame = 1;
|
||||
else if(bullets[i].angle >= 48 && bullets[i].angle < 80) bullets[i].frame = 2;
|
||||
else if(bullets[i].angle >= 80 && bullets[i].angle < 112) bullets[i].frame = 3;
|
||||
else if(bullets[i].angle >= 112 && bullets[i].angle < 144) bullets[i].frame = 4;
|
||||
else if(bullets[i].angle >= 112 && bullets[i].angle < 176) bullets[i].frame = 5;
|
||||
else if(bullets[i].angle >= 176 && bullets[i].angle < 208) bullets[i].frame = 6;
|
||||
else if(bullets[i].angle >= 208 && bullets[i].angle < 240) bullets[i].frame = 7;
|
||||
else if(bullets[i].angle >= 240 && bullets[i].angle < 272) bullets[i].frame = 8;
|
||||
|
||||
// 256 - 512
|
||||
else if(bullets[i].angle >= 272 && bullets[i].angle < 304) { bullets[i].frame = 7; bullets[i].hFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 304 && bullets[i].angle < 336) { bullets[i].frame = 6; bullets[i].hFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 336 && bullets[i].angle < 368) { bullets[i].frame = 5; bullets[i].hFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 368 && bullets[i].angle < 400) { bullets[i].frame = 4; bullets[i].hFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 400 && bullets[i].angle < 432) { bullets[i].frame = 3; bullets[i].hFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 432 && bullets[i].angle < 464) { bullets[i].frame = 2; bullets[i].hFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 464 && bullets[i].angle < 496) { bullets[i].frame = 1; bullets[i].hFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 496 && bullets[i].angle < 528) { bullets[i].frame = 0; bullets[i].hFlip = TRUE; }
|
||||
|
||||
// 512 - 768
|
||||
else if(bullets[i].angle >= 528 && bullets[i].angle < 560) { bullets[i].frame = 1; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 560 && bullets[i].angle < 592) { bullets[i].frame = 2; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 592 && bullets[i].angle < 624) { bullets[i].frame = 3; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 624 && bullets[i].angle < 656) { bullets[i].frame = 4; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 656 && bullets[i].angle < 688) { bullets[i].frame = 5; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 688 && bullets[i].angle < 720) { bullets[i].frame = 6; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 720 && bullets[i].angle < 752) { bullets[i].frame = 7; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 752 && bullets[i].angle < 784) { bullets[i].frame = 8; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
||||
|
||||
// 768 - 1024
|
||||
else if(bullets[i].angle >= 784 && bullets[i].angle < 816) { bullets[i].frame = 7; bullets[i].vFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 816 && bullets[i].angle < 848) { bullets[i].frame = 6; bullets[i].vFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 848 && bullets[i].angle < 880) { bullets[i].frame = 5; bullets[i].vFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 880 && bullets[i].angle < 912) { bullets[i].frame = 4; bullets[i].vFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 912 && bullets[i].angle < 944) { bullets[i].frame = 3; bullets[i].vFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 944 && bullets[i].angle < 976) { bullets[i].frame = 2; bullets[i].vFlip = TRUE; }
|
||||
else if(bullets[i].angle >= 976 && bullets[i].angle < 1008) { bullets[i].frame = 1; bullets[i].vFlip = TRUE; }
|
||||
|
||||
SPR_setFrame(bullets[i].image, bullets[i].frame);
|
||||
SPR_setHFlip(bullets[i].image, bullets[i].hFlip);
|
||||
SPR_setVFlip(bullets[i].image, bullets[i].vFlip);
|
||||
}
|
||||
}
|
||||
|
||||
void spawnBullet(struct bulletSpawner spawner, void(*updater)){
|
||||
s16 i = -1;
|
||||
for(s16 j = 0; j < BULLET_COUNT; j++) if(!bullets[j].active && i == -1) i = j;
|
||||
if(i > -1){
|
||||
bullets[i].active = TRUE;
|
||||
bullets[i].pos.x = spawner.x;
|
||||
bullets[i].pos.y = spawner.y;
|
||||
bullets[i].speed = spawner.speed;
|
||||
bullets[i].angle = spawner.angle;
|
||||
bullets[i].player = spawner.player;
|
||||
bullets[i].explosion = FALSE;
|
||||
bullets[i].clock = 0;
|
||||
for(u8 j = 0; j < COUNT_INT; j++){
|
||||
bullets[i].bools[j] = spawner.bools[j];
|
||||
bullets[i].ints[j] = spawner.ints[j];
|
||||
bullets[i].fixes[j] = spawner.fixes[j];
|
||||
}
|
||||
if(spawner.vel.x || spawner.vel.y){
|
||||
bullets[i].vel.x = spawner.vel.x;
|
||||
bullets[i].vel.y = spawner.vel.y;
|
||||
} else {
|
||||
bullets[i].vel.x = fix32Mul(fix16ToFix32(cosFix16(spawner.angle)), spawner.speed);
|
||||
bullets[i].vel.y = fix32Mul(fix16ToFix32(sinFix16(spawner.angle)), spawner.speed);
|
||||
}
|
||||
bullets[i].updater = updater;
|
||||
bullets[i].dist = bullets[i].player ? 16 : (spawner.anim == 0 ? 4 : 7);
|
||||
bullets[i].image = SPR_addSprite(spawner.player ? &pBulletSprite : &bulletsSprite,
|
||||
getScreenX(bullets[i].pos.x, player.camera) - (spawner.player ? P_BULLET_OFF : BULLET_OFF),
|
||||
fix32ToInt(bullets[i].pos.y) - (spawner.player ? P_BULLET_OFF : BULLET_OFF),
|
||||
TILE_ATTR(gameOver ? PAL1 : PAL0, 0, 0, spawner.player && spawner.angle == 512 ? 1 : 0));
|
||||
if(spawner.anim) SPR_setAnim(bullets[i].image, spawner.anim);
|
||||
bullets[i].anim = spawner.anim;
|
||||
SPR_setDepth(bullets[i].image, spawner.player ? 7 : (spawner.top ? 3 : 4));
|
||||
doBulletRotation(i);
|
||||
}
|
||||
}
|
||||
|
||||
s32 bulletDist;
|
||||
#define BULLET_CHECK FIX32(32)
|
||||
static void collideWithEnemy(u8 i){
|
||||
for(s16 j = 0; j < ENEMY_COUNT; j++) {
|
||||
if(enemies[j].active){
|
||||
// Calculate wrapped distances
|
||||
fix32 deltaX = getWrappedDelta(bullets[i].pos.x, enemies[j].pos.x);
|
||||
fix32 deltaY = bullets[i].pos.y - enemies[j].pos.y;
|
||||
|
||||
// Quick bounding box check using wrapped distance
|
||||
if(deltaY >= -BULLET_CHECK && deltaY <= BULLET_CHECK &&
|
||||
deltaX >= -BULLET_CHECK && deltaX <= BULLET_CHECK){
|
||||
// Precise distance check
|
||||
bulletDist = getApproximatedDistance(
|
||||
fix32ToInt(deltaX),
|
||||
fix32ToInt(deltaY));
|
||||
if(bulletDist <= bullets[i].dist){
|
||||
killBullet(i);
|
||||
killEnemy(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void collideWithPlayer(u8 i){
|
||||
if(!bullets[i].player && bullets[i].active){
|
||||
fix32 deltaX = getWrappedDelta(bullets[i].pos.x, player.pos.x);
|
||||
fix32 deltaY = bullets[i].pos.y - player.pos.y;
|
||||
|
||||
s32 dist = getApproximatedDistance(
|
||||
fix32ToInt(deltaX),
|
||||
fix32ToInt(deltaY));
|
||||
if(dist <= 16){ // Player hit radius
|
||||
killBullet(i);
|
||||
player.lives--;
|
||||
if(player.lives <= 0) gameOver = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void updateBullet(u8 i){
|
||||
bullets[i].pos.x += bullets[i].vel.x;
|
||||
bullets[i].pos.y += bullets[i].vel.y;
|
||||
|
||||
// Wrap bullet position
|
||||
if(bullets[i].pos.x >= GAME_WRAP){
|
||||
bullets[i].pos.x -= GAME_WRAP;
|
||||
}
|
||||
if(bullets[i].pos.x < 0){
|
||||
bullets[i].pos.x += GAME_WRAP;
|
||||
}
|
||||
|
||||
if(bullets[i].clock > 0) bullets[i].updater(i);
|
||||
if(bullets[i].player) collideWithEnemy(i);
|
||||
else collideWithPlayer(i);
|
||||
if(bullets[i].active){
|
||||
s16 sx = getScreenX(bullets[i].pos.x, player.camera);
|
||||
s16 sy = fix32ToInt(bullets[i].pos.y);
|
||||
u8 off = bullets[i].player ? P_BULLET_OFF : BULLET_OFF;
|
||||
|
||||
// Set visibility to prevent VDP 512px wrap ghosting
|
||||
// bool onScreen = (sx >= VISIBLE_X_MIN && sx <= VISIBLE_X_MAX &&
|
||||
// sy >= VISIBLE_Y_MIN && sy <= VISIBLE_Y_MAX);
|
||||
// SPR_setVisibility(bullets[i].image, onScreen ? VISIBLE : HIDDEN);
|
||||
|
||||
SPR_setPosition(bullets[i].image, sx - off, sy - off);
|
||||
bullets[i].clock++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void updateBullets(){
|
||||
for(s16 i = 0; i < BULLET_COUNT; i++) if(bullets[i].active)
|
||||
updateBullet(i);
|
||||
}
|
||||
22
src/chrome.h
Normal file
22
src/chrome.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
char scoreStr[SCORE_LENGTH];
|
||||
u32 lastScore;
|
||||
|
||||
static void drawScore(){
|
||||
uintToStr(score, scoreStr, 1);
|
||||
VDP_drawText(scoreStr, 1, 1);
|
||||
}
|
||||
|
||||
void loadChrome(){
|
||||
drawScore();
|
||||
}
|
||||
|
||||
void updateChrome(){
|
||||
score++;
|
||||
if(score > 99999999) score = 0;
|
||||
if(lastScore != score){
|
||||
lastScore = score;
|
||||
drawScore();
|
||||
}
|
||||
VDP_clearText(1, 26, 4);
|
||||
VDP_drawText(debugStr, 1, 26);
|
||||
}
|
||||
70
src/enemies.h
Normal file
70
src/enemies.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
void spawnEnemy(u8 type){
|
||||
s16 i = -1;
|
||||
for(s16 j = 0; j < ENEMY_COUNT; j++) if(!enemies[j].active && i == -1) i = j;
|
||||
if(i > -1){
|
||||
enemies[i].active = TRUE;
|
||||
enemies[i].type = type;
|
||||
enemies[i].pos.x = FIX32(64);
|
||||
enemies[i].pos.y = FIX32(64);
|
||||
enemies[i].off = 16;
|
||||
enemies[i].image = SPR_addSprite(&butterflySprite,
|
||||
getScreenX(enemies[i].pos.x, player.camera) - enemies[i].off, fix32ToInt(enemies[i].pos.y) - enemies[i].off, TILE_ATTR(gameOver ? PAL1 : PAL0, 0, 0, 0));
|
||||
enemies[i].angle = 128;
|
||||
enemies[i].speed = FIX32(0.25);
|
||||
enemies[i].vel.x = fix32Mul(fix16ToFix32(cosFix16(enemies[i].angle)), enemies[i].speed);
|
||||
enemies[i].vel.y = fix32Mul(fix16ToFix32(sinFix16(enemies[i].angle)), enemies[i].speed);
|
||||
}
|
||||
}
|
||||
|
||||
static void boundsEnemy(u8 i){
|
||||
if(enemies[i].pos.y >= GAME_H_F - FIX32(enemies[i].off) || enemies[i].pos.y <= FIX32(enemies[i].off))
|
||||
enemies[i].vel.y *= -1;
|
||||
|
||||
if(enemies[i].pos.x >= GAME_WRAP){
|
||||
enemies[i].pos.x -= GAME_WRAP;
|
||||
}
|
||||
if(enemies[i].pos.x < 0){
|
||||
enemies[i].pos.x += GAME_WRAP;
|
||||
}
|
||||
}
|
||||
|
||||
static void updateEnemy(u8 i){
|
||||
boundsEnemy(i);
|
||||
enemies[i].pos.x += enemies[i].vel.x;
|
||||
enemies[i].pos.y += enemies[i].vel.y;
|
||||
|
||||
s16 sx = getScreenX(enemies[i].pos.x, player.camera);
|
||||
s16 sy = fix32ToInt(enemies[i].pos.y);
|
||||
|
||||
fix32 dx = getWrappedDelta(enemies[i].pos.x, player.pos.x);
|
||||
bool onScreen = (dx >= FIX32(-256) && dx <= FIX32(256));
|
||||
SPR_setVisibility(enemies[i].image, onScreen ? VISIBLE : HIDDEN);
|
||||
|
||||
SPR_setPosition(enemies[i].image, sx - enemies[i].off, sy - enemies[i].off);
|
||||
enemyCount++;
|
||||
}
|
||||
|
||||
void updateEnemies(){
|
||||
enemyCount = 0;
|
||||
for(s16 i = 0; i < ENEMY_COUNT; i++) if(enemies[i].active)
|
||||
updateEnemy(i);
|
||||
intToStr(enemyCount, debugStr, 1);
|
||||
}
|
||||
|
||||
void spawnEnemyAt(u8 type, fix32 x, fix32 y){
|
||||
s16 i = -1;
|
||||
for(s16 j = 0; j < ENEMY_COUNT; j++) if(!enemies[j].active && i == -1) i = j;
|
||||
if(i > -1){
|
||||
enemies[i].active = TRUE;
|
||||
enemies[i].type = type;
|
||||
enemies[i].pos.x = x;
|
||||
enemies[i].pos.y = y;
|
||||
enemies[i].off = 16;
|
||||
enemies[i].image = SPR_addSprite(&butterflySprite,
|
||||
getScreenX(enemies[i].pos.x, player.camera) - enemies[i].off, fix32ToInt(enemies[i].pos.y) - enemies[i].off, TILE_ATTR(gameOver ? PAL1 : PAL0, 0, 0, 0));
|
||||
enemies[i].angle = 128;
|
||||
enemies[i].speed = FIX32(2);
|
||||
enemies[i].vel.x = fix32Mul(fix16ToFix32(cosFix16(enemies[i].angle)), enemies[i].speed);
|
||||
enemies[i].vel.y = fix32Mul(fix16ToFix32(sinFix16(enemies[i].angle)), enemies[i].speed);
|
||||
}
|
||||
}
|
||||
142
src/global.h
Normal file
142
src/global.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
u32 clock;
|
||||
#define CLOCK_LIMIT 32000
|
||||
#define COUNT_INT 4
|
||||
|
||||
#define GAME_H_F FIX32(224)
|
||||
|
||||
// Section-based world size
|
||||
#define SECTION_SIZE FIX32(512) // Size of one section (512px)
|
||||
#define SECTION_COUNT 4 // Number of sections (N = 2, 4, 8, etc.)
|
||||
#define GAME_WRAP (SECTION_SIZE * SECTION_COUNT) // Total world width
|
||||
|
||||
u32 score, highScore;
|
||||
#define SCORE_LENGTH 8
|
||||
|
||||
#define FIRST_ROTATING_BULLET 3
|
||||
#define CAMERA_Y_MOD FIX32(112)
|
||||
|
||||
// Screen bounds for visibility (screen is 320x224)
|
||||
// VDP wraps sprites every 512px, so we must hide sprites outside screen
|
||||
#define VISIBLE_X_MIN 0
|
||||
#define VISIBLE_X_MAX 512
|
||||
#define VISIBLE_Y_MIN -32
|
||||
#define VISIBLE_Y_MAX 256
|
||||
|
||||
char debugStr[8];
|
||||
s16 emptyI;
|
||||
void EMPTY(s16 i){emptyI = i;}
|
||||
|
||||
bool gameOver, paused, started;
|
||||
s16 enemyCount;
|
||||
|
||||
// controls
|
||||
struct controls {
|
||||
bool left, right, up, down, a, b, c, start;
|
||||
};
|
||||
struct controls ctrl;
|
||||
void updateControls(u16 joy, u16 changed, u16 state){
|
||||
if(changed){}
|
||||
if(joy == JOY_1){
|
||||
ctrl.left = (state & BUTTON_LEFT);
|
||||
ctrl.right = (state & BUTTON_RIGHT);
|
||||
ctrl.up = (state & BUTTON_UP);
|
||||
ctrl.down = (state & BUTTON_DOWN);
|
||||
ctrl.a = (state & BUTTON_A);
|
||||
ctrl.b = (state & BUTTON_B);
|
||||
ctrl.c = (state & BUTTON_C);
|
||||
ctrl.start = (state & BUTTON_START);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// player
|
||||
struct playerStruct {
|
||||
Vect2D_f32 pos, vel, last;
|
||||
s16 shotAngle;
|
||||
u8 lives;
|
||||
fix32 camera;
|
||||
Sprite* image;
|
||||
};
|
||||
struct playerStruct player;
|
||||
|
||||
|
||||
// bullets
|
||||
#define BULLET_COUNT 64
|
||||
|
||||
struct bulletSpawner {
|
||||
fix32 x, y, speed;
|
||||
Vect2D_f32 vel;
|
||||
s16 angle, anim;
|
||||
bool top, player;
|
||||
bool bools[COUNT_INT];
|
||||
s16 ints[COUNT_INT];
|
||||
fix32 fixes[COUNT_INT];
|
||||
};
|
||||
struct bullet {
|
||||
bool active, player, explosion, top, vFlip, hFlip;
|
||||
fix32 speed;
|
||||
Vect2D_f32 pos, vel;
|
||||
Sprite* image;
|
||||
s16 clock, angle, anim, frame;
|
||||
s16 dist;
|
||||
void (*updater)(s16);
|
||||
bool bools[COUNT_INT];
|
||||
s16 ints[COUNT_INT];
|
||||
fix32 fixes[COUNT_INT];
|
||||
};
|
||||
struct bullet bullets[BULLET_COUNT];
|
||||
|
||||
|
||||
// enemies
|
||||
#define ENEMY_COUNT 16
|
||||
|
||||
struct enemy {
|
||||
bool active;
|
||||
s16 clock, angle, anim, frame, off;
|
||||
fix32 speed;
|
||||
u8 type;
|
||||
Vect2D_f32 vel, pos;
|
||||
s16 dist;
|
||||
Sprite* image;
|
||||
void (*updater)(s16);
|
||||
bool bools[COUNT_INT];
|
||||
s16 ints[COUNT_INT];
|
||||
fix32 fixes[COUNT_INT];
|
||||
};
|
||||
struct enemy enemies[ENEMY_COUNT];
|
||||
|
||||
void killBullet(u8 i){
|
||||
bullets[i].active = FALSE;
|
||||
SPR_releaseSprite(bullets[i].image);
|
||||
}
|
||||
|
||||
void killEnemy(u8 i){
|
||||
enemies[i].active = FALSE;
|
||||
SPR_releaseSprite(enemies[i].image);
|
||||
}
|
||||
|
||||
// Calculate shortest X distance accounting for world wrap
|
||||
// Returns distance in the range [-GAME_WRAP/2, GAME_WRAP/2]
|
||||
static fix32 getWrappedDelta(fix32 a, fix32 b) {
|
||||
fix32 delta = a - b;
|
||||
// If distance is more than half the world, go the other way
|
||||
if (delta > GAME_WRAP / 2) {
|
||||
delta -= GAME_WRAP;
|
||||
} else if (delta < -GAME_WRAP / 2) {
|
||||
delta += GAME_WRAP;
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
|
||||
// Safe screen X calculation handling wrap edge cases
|
||||
// Returns screen coordinate for an entity, accounting for entities that just wrapped
|
||||
static s16 getScreenX(fix32 worldX, fix32 camera) {
|
||||
fix32 screenX = worldX - camera;
|
||||
// Handle entity that just wrapped (temporarily far off-screen)
|
||||
if (screenX < FIX32(-256)) {
|
||||
screenX += GAME_WRAP;
|
||||
} else if (screenX > FIX32(256)) {
|
||||
screenX -= GAME_WRAP;
|
||||
}
|
||||
return fix32ToInt(screenX);
|
||||
}
|
||||
51
src/main.c
Normal file
51
src/main.c
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#include <genesis.h>
|
||||
#include <resources.h>
|
||||
|
||||
#include "global.h"
|
||||
#include "background.h"
|
||||
#include "bullets.h"
|
||||
#include "enemies.h"
|
||||
#include "player.h"
|
||||
#include "stage.h"
|
||||
#include "chrome.h"
|
||||
#include "start.h"
|
||||
|
||||
static void loadInternals(){
|
||||
JOY_init();
|
||||
JOY_setEventHandler(&updateControls);
|
||||
SPR_init();
|
||||
VDP_setPlaneSize(128, 32, TRUE);
|
||||
VDP_loadFont(font.tileset, DMA);
|
||||
PAL_setPalette(PAL0, font.palette->data, DMA);
|
||||
PAL_setPalette(PAL1, shadow.palette->data, CPU);
|
||||
VDP_setTextPriority(1);
|
||||
}
|
||||
|
||||
void loadGame(){
|
||||
loadBackground();
|
||||
loadPlayer();
|
||||
loadChrome();
|
||||
loadStage();
|
||||
}
|
||||
|
||||
static void updateGame(){
|
||||
updateChrome();
|
||||
updateBackground();
|
||||
updateEnemies();
|
||||
updatePlayer();
|
||||
updateBullets();
|
||||
}
|
||||
|
||||
int main(bool hardReset){
|
||||
loadInternals();
|
||||
loadGame();
|
||||
// loadStart();
|
||||
while(1){
|
||||
updateGame();
|
||||
clock++;
|
||||
if(clock >= CLOCK_LIMIT) clock = 600;
|
||||
SPR_update();
|
||||
SYS_doVBlankProcess();
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
153
src/player.h
Normal file
153
src/player.h
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
#define PLAYER_SPEED FIX32(5)
|
||||
#define PLAYER_SPEED_NORM fix32Mul(PLAYER_SPEED, FIX32(0.707))
|
||||
|
||||
#define PLAYER_SPEED_FOCUS FIX32(3)
|
||||
#define PLAYER_SPEED_FOCUS_NORM fix32Mul(PLAYER_SPEED_FOCUS, FIX32(0.707))
|
||||
|
||||
#define PLAYER_ACCEL PLAYER_SPEED >> 3
|
||||
#define PLAYER_ACCEL_FOCUS PLAYER_SPEED_FOCUS >> 3
|
||||
|
||||
#define PLAYER_OFF 16
|
||||
#define PLAYER_BOUND_Y FIX32(PLAYER_OFF)
|
||||
#define PLAYER_BOUND_H FIX32(224 - PLAYER_OFF)
|
||||
|
||||
#define CAMERA_X FIX32(80)
|
||||
#define CAMERA_W FIX32(240)
|
||||
|
||||
#define SHOT_INTERVAL 15
|
||||
|
||||
s16 shotClock;
|
||||
fix32 screenX;
|
||||
|
||||
fix32 playerSpeed, playerSpeedNorm;
|
||||
fix32 playerVelX; // Track actual X velocity for momentum
|
||||
|
||||
static void movePlayer(){
|
||||
// Y-axis stays instant
|
||||
player.vel.y = 0;
|
||||
|
||||
// Determine target X speed based on input
|
||||
fix32 targetVelX = 0;
|
||||
if(ctrl.left || ctrl.right || ctrl.up || ctrl.down){
|
||||
if(ctrl.b){
|
||||
playerSpeed = PLAYER_SPEED_FOCUS;
|
||||
playerSpeedNorm = PLAYER_SPEED_FOCUS_NORM;
|
||||
} else {
|
||||
playerSpeed = PLAYER_SPEED;
|
||||
playerSpeedNorm = PLAYER_SPEED_NORM;
|
||||
}
|
||||
player.last.x = player.pos.x;
|
||||
if(ctrl.left || ctrl.right){
|
||||
if(!ctrl.a) player.shotAngle = ctrl.left ? 512 : 0;
|
||||
targetVelX = ctrl.left ? -playerSpeed : playerSpeed;
|
||||
}
|
||||
|
||||
// Y velocity (instant)
|
||||
if(ctrl.up) player.vel.y = -playerSpeed;
|
||||
else if(ctrl.down) player.vel.y = playerSpeed;
|
||||
}
|
||||
|
||||
// Apply acceleration toward target X velocity
|
||||
if(playerVelX < targetVelX){
|
||||
playerVelX += ctrl.b ? PLAYER_ACCEL_FOCUS : PLAYER_ACCEL;
|
||||
if(playerVelX > targetVelX) playerVelX = targetVelX;
|
||||
} else if(playerVelX > targetVelX){
|
||||
playerVelX -= ctrl.b ? PLAYER_ACCEL_FOCUS : PLAYER_ACCEL;
|
||||
if(playerVelX < targetVelX) playerVelX = targetVelX;
|
||||
}
|
||||
|
||||
player.vel.x = playerVelX;
|
||||
|
||||
// Normalize if diagonal
|
||||
if(player.vel.x != 0 && player.vel.y != 0){
|
||||
player.vel.x = fix32Mul(player.vel.x, FIX32(0.707));
|
||||
player.vel.y = fix32Mul(player.vel.y, FIX32(0.707));
|
||||
}
|
||||
|
||||
// Apply movement (always, for momentum to work during deceleration)
|
||||
player.pos.x += player.vel.x;
|
||||
player.pos.y += player.vel.y;
|
||||
|
||||
// Update facing direction when moving horizontally
|
||||
if(ctrl.a){
|
||||
SPR_setHFlip(player.image, player.shotAngle != 0);
|
||||
} else {
|
||||
if(player.vel.x < 0){
|
||||
SPR_setHFlip(player.image, TRUE);
|
||||
} else if(player.vel.x > 0){
|
||||
SPR_setHFlip(player.image, FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void boundsPlayer(){
|
||||
if(player.pos.y < PLAYER_BOUND_Y)
|
||||
player.pos.y = PLAYER_BOUND_Y;
|
||||
else if(player.pos.y > PLAYER_BOUND_H)
|
||||
player.pos.y = PLAYER_BOUND_H;
|
||||
|
||||
if(player.pos.x >= GAME_WRAP){
|
||||
player.pos.x -= GAME_WRAP;
|
||||
player.camera -= GAME_WRAP;
|
||||
}
|
||||
|
||||
if(player.pos.x <= 0){
|
||||
player.pos.x += GAME_WRAP;
|
||||
player.camera += GAME_WRAP;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void cameraPlayer(){
|
||||
screenX = player.pos.x - player.camera;
|
||||
if(screenX < CAMERA_X && player.vel.x < 0)
|
||||
player.camera += player.vel.x;
|
||||
else if(screenX > CAMERA_W && player.vel.x > 0)
|
||||
player.camera += player.vel.x;
|
||||
}
|
||||
|
||||
static void shootPlayer(){
|
||||
if(ctrl.a && shotClock == 0){
|
||||
struct bulletSpawner spawner = {
|
||||
.x = player.pos.x,
|
||||
.y = player.pos.y,
|
||||
.anim = 0,
|
||||
.speed = FIX32(12),
|
||||
.angle = player.shotAngle,
|
||||
.player = TRUE
|
||||
};
|
||||
void updater(u8 i){
|
||||
if(bullets[i].clock >= 16) killBullet(i);
|
||||
}
|
||||
spawnBullet(spawner, updater);
|
||||
shotClock = SHOT_INTERVAL;
|
||||
} else if(shotClock > 0) shotClock--;
|
||||
}
|
||||
|
||||
void loadPlayer(){
|
||||
player.shotAngle = 0;
|
||||
player.camera = 0;
|
||||
player.pos.x = FIX32(128);
|
||||
player.pos.y = FIX32(112);
|
||||
playerVelX = 0;
|
||||
player.lives = 3;
|
||||
player.image = SPR_addSprite(&sakuyaSprite,
|
||||
fix32ToInt(player.pos.x) - PLAYER_OFF,
|
||||
fix32ToInt(player.pos.y) - PLAYER_OFF,
|
||||
TILE_ATTR(PAL0, 0, 0, 0));
|
||||
}
|
||||
|
||||
void updatePlayer(){
|
||||
movePlayer();
|
||||
boundsPlayer();
|
||||
cameraPlayer();
|
||||
shootPlayer();
|
||||
|
||||
s16 sx = getScreenX(player.pos.x, player.camera);
|
||||
s16 sy = fix32ToInt(player.pos.y);
|
||||
|
||||
|
||||
SPR_setPosition(player.image, sx - PLAYER_OFF, sy - PLAYER_OFF);
|
||||
|
||||
intToStr(fix32ToInt(player.pos.x), debugStr, 1);
|
||||
}
|
||||
3
src/stage.h
Normal file
3
src/stage.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
void loadStage(){
|
||||
spawnEnemy(0);
|
||||
}
|
||||
7
src/start.h
Normal file
7
src/start.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#define START_I 8
|
||||
|
||||
void loadStart(){
|
||||
VDP_drawImageEx(BG_A, &logo, TILE_ATTR_FULL(PAL0, 0, 0, 0, START_I), 6, 10, FALSE, FALSE);
|
||||
VDP_drawText("press any button", 12, 16);
|
||||
VDP_drawText(" 2026 T.BODDY ", 12, 18);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue