;*********************************
;* Jungle Jag - JagCode 500 Game *
;*********************************
;
; by Cedric Bourse, (a.k.a Orion_) [2007]
;
; http://onorisoft.info/
;
; This Source Code is licensed under the term of the following Creative Commons License:
; http://creativecommons.org/licenses/by-nc/3.0/
;
; You are free:
;  * to Share : to copy, distribute and transmit the work
;  * to Remix : to adapt the work
;
; Under the following conditions:
;  * Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
;  * Noncommercial. You may not use this work for commercial purposes.
;  * For any reuse or distribution, you must make clear to others the license terms of this work. The best way to do this is with a link to this web page.
;  * Any of the above conditions can be waived if you get permission from the copyright holder.
;  * Nothing in this license impairs or restricts the author's moral rights.
;

	.text

TILE_FALL_S	.equ	1
TILE_FALL_B	.equ	16
TILE_FALL_E	.equ	23
TILE_FALL_A1	.equ	68
TILE_FALL_A2	.equ	69
TILE_BLOC_S	.equ	34
TILE_BLOC_E	.equ	35

JUMP_ACCEL	.equ	6	; Jump Acceleration
MAX_VELOCITY	.equ	128	; Maximum Velocity
JUMP_PREC	.equ	5	; Velocity Fixed Point Precision

COEUR_X		.equ	16
COEUR_Y		.equ	256-16

ITEMS_X		.equ	COEUR_X+(16*(3+2))	; 3 lives + 2 blank space
ITEMS_Y		.equ	COEUR_Y

WIN_TILE_S	.equ	48
WIN_TILE_E	.equ	49

GamePlay:

	bsr	ReplaceItems
	bsr	MoveEnnemies

;----------------------
; Player Pad Handling -
;----------------------

; JoyPad 0:	    3322 2222 2222 1111 1111 1100 0000 0000
;		    1098 7654 3210 9876 5432 1098 7654 3210
; $810x	->	e - xxxx ><v^ xxxx xxxx xxxx xxxx xxxx xxAP
;		d - xxxx *741 xxxx xxxx xxxx xxxx xxxx xxBx

;------------
;- PAD JUMP -
;------------

	tst.b	hero_mort		; No control when dead !!
	bne	.Out

	move.w	#$810d,JOYSTICK		; Read Pad 1, B button
	move.l	JOYSTICK,d7		; Get B Button 0 = pressed, 1 = not pressed

	tst.b	hero_liane
	bne.s	.CheckArrow

	btst.l	#1,d7			; B Button (jump)
	bne.s	.nojump

	tst.b	prev_jump		; Wait button released if already pressed
	bne.s	.CheckArrow

	tst.w	hero_jump		; don't set jump if falling or in jump
	bne.s	.nojump
	move.w	#-1,hero_jump		; Set jump
	move.w	#-MAX_VELOCITY,jump_vel	; jump initial velocity

	lea	SFX_Jump,a0
	lea	SFX_JumpE,a1
	bsr	AddSound

	st	prev_jump		; Flag button pressed

	bra.s	.CheckArrow

.nojump:
	tst.w	hero_jump
	bne.s	.CheckArrow

	sf	prev_jump		; Flag button released

;-------------
;- PAD LIANE -
;-------------

.CheckArrow:
	move.w	#$810e,JOYSTICK		; Read Pad 1, Arrow
	move.l	JOYSTICK,d7		; Get Buttons and Arrow states 0 = pressed, 1 = not pressed

	btst.l	#1,d7			; A Button (Liane)
	bne.s	.nolianeprev2

	tst.b	hero_liane
	bne.s	.lokK
	tst.b	prev_liane
	bne.s	.nolianeprev

.lokK:
	lea	current_map,a0
	move.w	(a0),d0			; Map XSize
	move.l	8(a0),a1		; Layer 1

	move.w	hero_x,d1		; Hero position
	move.w	hero_y,d2

	addi.w	#15,d2			; Y Calibration

	lsr.w	#4,d1			; in tile
	lsr.w	#4,d2

	addq.w	#1,d1			; X++

	mulu	d0,d2			; y * map xsize
	add.w	d1,d2			; + previously computed x
	move.b	0(a1,d2.w),d4		; Test the liane from layer 1

	cmpi.b	#LIANE_TILE_ID,d4	; Liane
	blt.s	.nolianeprev
	cmpi.b	#LIANE_TILE_ID+LIANE_NUMBER,d4
	bgt.s	.nolianeprev

	tst.b	hero_liane
	bne.s	.ArrowUpDown

	andi.w	#-16,hero_x		; Recadre le hero sur la liane
	addi.w	#9,hero_x
	clr.w	jump_vel		; fall initial velocity
	clr.w	hero_jump		; The hero is not falling !

	st	hero_liane		; Flag button pressed
	st	prev_liane

	bra.s	.ArrowUpDown

.nolianeprev2:
	sf	prev_liane
.nolianeprev:
	sf	hero_liane		; Flag button released
	clr.w	hero_moveliane
	bra.s	.noliane

;------------------
;- PAD ARROW MOVE -
;------------------

.ArrowUpDown:
	btst.l	#25,d7			; Down
	bne.s	.CheckUp
	move.w	#1,hero_moveliane
	bra.s	.Out

.CheckUp:
	btst.l	#24,d7			; Up
	bne.s	.nomove
	move.w	#-1,hero_moveliane
	bra.s	.Out

.noliane:
	btst.l	#27,d7			; Right
	bne.s	.CheckLeft
	st	hero_move
	move.w	#1,hero_sens

	move.w	hero_sid,d0
	bsr	UnSetMirrorSprite

	bra.s	.Out

.CheckLeft:
	btst.l	#26,d7			; Left
	bne.s	.nomove
	st	hero_move
	move.w	#-1,hero_sens

	move.w	hero_sid,d0
	bsr	SetMirrorSprite

	bra.s	.Out

.nomove:
	clr.w	hero_anim
	clr.w	hero_moveliane
	sf	hero_move

.Out:


;---------------------------------
; Do Collisions before Hero Move - (so the collision can stop the Hero Moves !)
;---------------------------------

	bsr	Collision



;---------------------
; Jump/Fall Handling -
;---------------------

	tst.w	hero_jump		; If jump or fall
	beq.s	.vel_crop

	cmpi.w	#MAX_VELOCITY,jump_vel	; Don't increase velocity if > MAX_VELOCITY
	bge.s	.vel_crop
	addi.w	#JUMP_ACCEL,jump_vel	; velocity += acceleration
.vel_crop:



;------------------------
; Level Board Scrolling -
;------------------------

	tst.b	hero_move		; If hero move
	beq.s	.nscr

	move.w	hero_x,d0
	cmpi.w	#(320-32)/2,d0		; If hero_X > the middle of the screen
	blt.s	.nscr
	cmp.w	board_limit,d0		; and hero_X < end of board minus half screen
	bgt.s	.nscr

	move.w	board_limit,d0
	subi.w	#146,d0
	neg	d0
	move.w	scroll_x,d1

	cmpi.w	#-1,hero_sens
	bne.s	.nmLBS
	tst.w	d1
	beq.s	.nscr
	bra.s	.nmLBS1

.nmLBS:
	cmp.w	d1,d0
	beq.s	.nscr
.nmLBS1:
	move.w	hero_sens,d0		; Scroll in reverse of the hero_x sens
	sub.w	d0,scroll_x

.nscr:
	moveq	#1,d0			; ADD -1 HERE IF NO BACKGROUND
	move.w	scroll_x,d1
	bsr	SetSpriteX


;----------------
;- Gestion Mort -
;----------------

	tst.b	hero_mort
	beq	.gmn
	cmpi.w	#10,hero_anim	; 10 frames dead
	bne.s	.gmn

	move.w	scroll_x,d1
	andi.w	#-2,d1

	tst.w	d1		; If dead and animation end then
	beq.s	.respawn

	addq.w	#2,d1		; scroll until the start of the level !
	move.w	d1,scroll_x

	bra.s	.gmn
.respawn:			; Else, respawn !
	subq.w	#1,coeur	; live--

	move.w	coeur_sid,d0	; Hide one heart (coeur)
	add.w	coeur,d0
	moveq	#-32,d1
	bsr	SetSpriteX

	tst.w	coeur
	bne.s	.hadlive

	st	game_over
	addq.w	#1,phase
	move.w	#256,fade_cnt

.hadlive:
	sf	hero_mort
	clr.w	hero_anim
	move.w	hero_ox,d0	; Restore original hero position
	move.w	hero_oy,d1
	move.w	d0,hero_x
	move.w	d1,hero_y
.gmn:


;--------------------------
; Hero/Ennemies Animation -
;--------------------------

	move.w	hero_wait,d0	; Animation only Each 8 vbl
	addq.w	#1,d0
	andi.w	#7,d0
	move.w	d0,hero_wait
	bne.s	.nanim

	bsr	AnimEnnemies

	move.w	hero_anim,d1
	tst.b	hero_mort
	beq.s	.nmrta
	cmpi.w	#10,d1		; 10 frames dead
	beq.s	.nanim
	addq.w	#1,d1
	bne.s	.nan
.nmrta:	addq.w	#1,d1
	cmpi.w	#7,d1		; 7 frames loop
	bne.s	.nan
	moveq	#0,d1
.nan:	move.w	d1,hero_anim
.nanim:

;---

	moveq	#0,d1		; but Update Anim frame each VBL ! (for jump/fall)

	tst.b	hero_mort
	bne.s	.animmort

	tst.b	hero_liane
	bne.s	.animliane

	tst.w	hero_jump	; No anim during jump/fall
	blt.s	.animjump
	bgt.s	.animfall

	tst.b	hero_move
	beq.s	.show_anim

	move.w	hero_anim,d1
	addq.w	#1,d1		; anim from frame 1
	mulu.w	#32*31,d1	; frame data size
	bra.s	.show_anim

.animmort:
	move.w	hero_anim,d1
	addi.w	#11,d1		; anim from frame 11 (dead)
	mulu.w	#32*31,d1	; frame data size
	bra.s	.show_anim

.animliane:
	tst.w	hero_moveliane
	beq.s	.lnmv
	move.w	hero_anim,d1
	andi.w	#1,d1
.lnmv:	addq.w	#8,d1		; anim from frame 8
	mulu.w	#32*31,d1	; frame data size
	bra.s	.show_anim

.animfall:
	move.w	#32*31*10,d1	; Fall frame
	bra.s	.show_anim

.animjump:
	move.w	#32*31*9,d1	; Jump frame

.show_anim:
	move.w	hero_sid,d0	; hero sprite id
	add.l	#hero,d1
	bsr	SetSpriteData


;------------------------------
; Hero Move and Sprite Update -
;------------------------------

	tst.b	hero_move		; only if moving :p
	beq.s	.nxmove
	move.w	hero_sens,d0		; X Move
	add.w	d0,hero_x
.nxmove:

	tst.b	hero_liane
	beq.s	.nlimove
	move.w	hero_moveliane,d0
	add.w	d0,hero_y
.nlimove:

	tst.w	hero_jump
	beq.s	.nymove
	move.w	jump_vel,d0		; Y Move (velocity)
	asr.w	#JUMP_PREC,d0		; Signed Fixed Point
	add.w	d0,hero_y
.nymove:

	moveq	#0,d2			; Mirror Mode X Add Right

	tst.w	hero_sens
	bge.s	.nhmright
	moveq	#32,d2			; Mirror Mode X Add Left
.nhmright:

	move.w	hero_sid,d0

	move.w	hero_x,d1
	add.w	d2,d1			; Mirror Mode X Add
	add.w	scroll_x,d1		; Add Board Scrolling Position

	move.w	hero_y,d2
	sub.w	#31-16,d2

	bsr	SetSpriteXY		; Update Sprite Position

	rts

;**************************


;-------------
; Collisions -
;-------------

Collision:

; Pre Computation

	lea	current_map,a0
	move.w	(a0),d0		; Map XSize
	move.l	8(a0),a1	; Layer 1
	move.l	4(a0),a0	; Layer 0

;--------------
; ITEMS Check -
;--------------
	move.w	hero_x,d1	; Hero position
	move.w	hero_y,d2

	lsr.w	#4,d1		; in tile
	lsr.w	#4,d2

	addq.w	#1,d1		; X++

	mulu	d0,d2		; y * map xsize
	add.w	d1,d2		; + previously computed x
	move.b	0(a1,d2.w),d4	; Test the item from layer 1
	bsr	.CheckItemColid
	sub.w	d0,d2		; Previous Y Tile
	move.b	0(a1,d2.w),d4	; Test the item from layer 1
	bsr	.CheckItemColid
	bra.s	.itemend

.CheckItemColid:
	cmpi.b	#ITEM_TILE_ID,d4	; Diams
	blt.s	.noitem
	cmpi.b	#ITEM_TILE_ID+ITEM_NUMBER,d4
	bgt.s	.noitem

	subq.w	#1,nitems

	move.l	a0,-(a7)
	move.l	a1,-(a7)
	lea	SFX_Diam,a0
	lea	SFX_DiamE,a1
	bsr	AddSound
	move.l	(a7)+,a1
	move.l	(a7)+,a0

	moveq	#0,d5
	move.b	d4,d5
	subi.w	#ITEM_TILE_ID,d5
	lea	ItemsTable,a2
.ihere:	move.w	(a2)+,d4
	blt.s	.noitem		; problem here O_o item not found in items list ?? Should not happen !
	move.w	(a2)+,d4	; item id
	cmp.w	d4,d5
	bne.s	.nextitem	; Found our item !
	move.w	nitems,d4
	lsl.w	#4,d4
	addi.w	#ITEMS_X,d4
	move.w	d4,(a2)+	; Change X !
	move.w	#ITEMS_Y,(a2)+	; Change Y !
	addq.w	#1,(a2)		; Taken !
	clr.b	0(a1,d2.w)	; and don't forget to clear it on map !!
	bra.s	.noitem		; End with it.
.nextitem:
	addq.l	#2*3,a2		; next !
	bra.s	.ihere

.noitem:
	rts
.itemend:


;---------------------
; Frontal Bloc Check -
;---------------------

	move.w	hero_x,d1	; Hero position
	move.w	hero_y,d2

	add.w	hero_sens,d1	; add X Sens then check colision (wont be added to hero_x if collision !)
	addi.w	#15,d2		; Collision Adjust in Y

	lsr.w	#4,d1		; in tile
	lsr.w	#4,d2

	move.w	d0,d3		; Right Border Adjust
	subq.w	#2,d3

	cmpi.w	#-8,d1		; Border Left Bloc
	ble.s	.bloc
	cmp.w	d3,d1		; Border Right Bloc
	bge.s	.bloc

	addq.w	#1,d1		; Collision Adjust in X

	mulu	d0,d2		; y * map xsize
	add.w	d1,d2		; + previously computed x
	move.b	0(a0,d2.w),d4	; Test the tile in front of the hero
	cmpi.b	#TILE_FALL_S,d4
	blt.s	.nobloc
	cmpi.b	#TILE_FALL_B,d4
	ble.s	.bloc
	cmpi.b	#TILE_BLOC_S,d4
	blt.s	.nobloc
	cmpi.b	#TILE_BLOC_E,d4
	ble.s	.bloc
	bra.s	.nobloc
.bloc:
	sf	hero_move	; Stop Hero Moves !

.nobloc:

;------------------
;- WIN GATE CHECK -
;------------------

	cmpi.b	#WIN_TILE_S,d4
	blt.s	.nowin
	cmpi.b	#WIN_TILE_E,d4
	bgt.s	.nowin

	tst.w	nitems
	bne.s	.nowin
	tst.w	hero_jump
	bne.s	.nowin		; wait fall

	st	win		; WINNER !!
	addq.w	#1,phase
	move.w	#256,fade_cnt

.nowin:


;------------------------
; Falling Gravity Check -
;------------------------

	tst.b	hero_liane	; no gravity when on liane
	bne.s	.colend

	tst.w	jump_vel	; If (velocity >= 0) [Jump End]
	blt.s	.colend

	move.w	hero_x,d1	; Hero position
	move.w	hero_y,d2

	lsr.w	#4,d1		; in tile
	lsr.w	#4,d2

	addq.w	#1,d1		; X+1
	addq.w	#1,d2		; Y+1

	mulu	d2,d0		; y * map xsize
	add.w	d1,d0		; +x
	move.b	0(a0,d0.w),d4	; Test the tile under the hero
	cmpi.b	#TILE_FALL_S,d4
	blt.s	.fall
	cmpi.b	#TILE_FALL_E,d4
	bgt.s	.fall
	bra.s	.nfall

.fall:
	cmpi.b	#TILE_FALL_A1,d4
	beq.s	.nfall
	cmpi.b	#TILE_FALL_A2,d4
	beq.s	.nfall

	move.w	#1,hero_jump	; The hero is falling !

	bra.s	.colend

.nfall:
	clr.w	jump_vel	; fall initial velocity
	clr.w	hero_jump	; The hero is not falling !
	addi.w	#8,hero_y	; (prevent upper bound to be taken)
	andi.w	#-16,hero_y	; ReAdjust Y position to 16 pixels boundary [tile size]

.colend:
	rts

;**************************


;*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*


;----------------
; Anim Ennemies -
;----------------

AnimEnnemies:
	lea	EnnemyTable,a1
	lea	EnnemyInfo,a2
.eanim:	move.w	(a1)+,d0	; Get Sprite ID
	blt.s	.eaend		; If end then go out
	move.w	8(a1),d1	; anim frame
	addq.w	#1,d1
	andi.w	#3,d1		; 4 frames
	move.w	d1,8(a1)
	move.w	4(a1),d2	; Ennemy type
	move.w	4(a2,d2.w),d3	; X*
	mulu	6(a2,d2.w),d3	;   Y
	mulu	d3,d1		;    *frame
	add.l	0(a2,d2.w),d1	; Add ennemy gfx
	bsr	SetSpriteData
	lea	7*2(a1),a1
	bra.s	.eanim
.eaend:
	rts

;**************************


;----------------
; Move Ennemies - & also check for hero colision with ennemies
;----------------

MoveEnnemies:
	lea	EnnemyTable,a1
	lea	EnnemyInfo,a2
.emove:	move.w	(a1)+,d0	; Get Sprite ID
	blt	.etend		; If end then go out

	move.w	(a1),d1		; Get X

	moveq	#0,d4		; Mirror Mode XSize = 0

	move.w	6(a1),d3	; Sens
	bne.s	.moveright

	subq.w	#1,d1		; Move Left here
	cmp.w	10(a1),d1	; X == Xstart ?
	bne.s	.moveglobl
	move.w	#1,6(a1)	; Reverse Sens !
	move.w	d0,d7
	bsr	SetMirrorSprite
	move.w	d7,d0

	move.w	4(a1),d3	; Ennemy type
	move.w	4(a2,d3.w),d4	; X Size (Jaguar Mirror Mode "specificity" ...)

	bra.s	.moveglobl

.moveright:
	addq.w	#1,d1		; Move Right here
	cmp.w	12(a1),d1	; X == Xend ?
	bne.s	.movegloblB
	clr.w	6(a1)		; Reverse Sens !
	move.w	d0,d7
	bsr	UnSetMirrorSprite
	move.w	d7,d0

	bra.s	.moveglobl

.movegloblB:
	move.w	4(a1),d3	; Ennemy type
	move.w	4(a2,d3.w),d4	; X Size (Jaguar Mirror Mode "specificity" ...)

.moveglobl:
	move.w	d1,(a1)		; new Xt
	add.w	scroll_x,d1	; Add Board X Scroll Position
	add.w	d4,d1		; Add XSize (Jaguar Mirror Mode "specificity" ...)
	bsr	SetSpriteX

;- Check Colision with hero

	tst.b	hero_mort	; don't check if already dead
	bne.s	.ncolid

	move.w	(a1),d1		; Get Ennemy X
	move.w	2(a1),d2	; Get Ennemy Y
	move.w	hero_x,d3
	move.w	hero_y,d4
	lsr.w	#4,d1
	lsr.w	#4,d2
	lsr.w	#4,d3
	lsr.w	#4,d4

	cmp.w	d1,d3		; X Match ?
	bne.s	.ncolid
	cmp.w	d2,d4		; Y Match ?
	bne.s	.ncolid

	move.l	a1,-(a7)
	move.l	a2,-(a7)
	lea	SFX_Dead,a0
	lea	SFX_DeadE,a1
	bsr	AddSound
	move.l	(a7)+,a2
	move.l	(a7)+,a1

	st	hero_mort	; If match, then DEAD !!!!
	clr.w	hero_anim
	sf	hero_move
	clr.w	hero_liane

;-
.ncolid:
	lea	7*2(a1),a1	; Next !

	bra	.emove
.etend:
	rts

;***********************************


;----------------
; Replace Items -
;----------------

ReplaceItems:
	lea	ItemsTable,a1
.imove:	move.w	(a1)+,d0	; Get Sprite ID
	blt.s	.itend		; If end then go out

	addq.l	#2,a1		; Skip Item Type

	move.w	(a1)+,d1	; Get X
	move.w	(a1)+,d2	; Get Y
	tst.w	(a1)+		; If Taken, then don't add scroll_x
	bne.s	.inadd

	add.w	scroll_x,d1	; Add Board X Scroll Position
.inadd:	bsr	SetSpriteXY

	bra.s	.imove
.itend:
	rts

;***********************************
