Jump to content

  • Log In with Google      Sign In   
  • Create Account

New Old Things

A C64 Game - Final Step

Posted by , in C64 17 May 2013 - - - - - - · 2,478 views

And thus this create a game series ends...

We fix the last few bugs (score was not properly reset on replay, music was stuck on saving the scores). Now the game is complete and can be enjoyed as it was meant to be!

Attached Image

Oh joy, Smila (the graphician) actually touched up the game even more for a retail release. It has been out for a few weeks now, but if you didn't know, go here:
Psytronik (for digital download, tape or disk) or to RGCD (cartridge with nifty packaging and stickers).
Or simply marvel in all the nice games that are still being made for older computers.

There you can actually buy a severely enhanced version (more two player game modes, better graphic, gameplay enhancements) as either digital download for emulators, or as a real tape, disk and finally as cartridge!

Thank you for your encouragements throughout, and keep on coding ;)



Previous Step

Attached Files




A C64 Game - Step 98

Posted by , in C64 03 May 2013 - - - - - - · 1,250 views

And here's the final bit, the extro part. Never have the player play through your whole game and put "Game Over" there. At the minimum a nice message is required Posted Image

And yes, it's symbolic at 98, since there's always some bugs left to fix. The last two steps will be mostly bug fixes.

Since it's the extro (and not even particularely impressing) I'll refrain to show the screenshot (and the code update) ;)

Just for explanation, to hide the sprite behind some chars there's a priority bit. This is used to hide the main object when it goes behind the hills.



Previous Step Next Step

Attached Files




A C64 Game - Step 97

Posted by , in C64 26 April 2013 - - - - - - · 1,168 views

And here's a little gameplay update, the bats. The diagonal movement was too predictable, so now there's more randomness to it.

Attached Image

The bat will move in curves. On every end of a curve the new direction will be decided randomly. Two tables are enough, however due to the C64 using two bit complement negative values are annoying to handle. Therefor I went the naive route and simply added code for every case. Ugly, but it works Posted Image
;------------------------------------------------------------

;simply move diagonal

;------------------------------------------------------------

!zone BehaviourBatDiagonal

BehaviourBatDiagonal

		  jsr HandleHitBack

		  beq .NoHitBack

		  rts
.RandomDir

		  jsr GenerateRandomNumber

		  and #$07

		  sta SPRITE_DIRECTION,x

		  inc SPRITE_DIRECTION,x

		 

		  lda #0

		  sta SPRITE_MOVE_POS,x

		  rts


.NoHitBack		 

		  lda DELAYED_GENERIC_COUNTER

		  and #$03

		  bne .NoAnimUpdate

		 

		  inc SPRITE_ANIM_POS,x

		  lda SPRITE_ANIM_POS,x

		  and #$03

		  sta SPRITE_ANIM_POS,x

		 

		  tay

		  lda BAT_ANIMATION,y

		  sta SPRITE_POINTER_BASE,x

		 

.NoAnimUpdate		 

		  lda SPRITE_DIRECTION,x

		  beq .RandomDir

		  cmp #1

		  beq .MoveCCWWN

		  cmp #2

		  beq .MoveCCWSW

		  cmp #3

		  beq .MoveCCWES

		  cmp #4

		  beq .MoveCCWNE

		  cmp #5

		  bne +

		  jmp .MoveCWWS

+		 

		  cmp #6

		  bne +

		  jmp .MoveCWNW

+		 

		  cmp #7

		  bne +

		  jmp .MoveCWEN

+		 

		  cmp #8

		  bne +

		  jmp .MoveCWSE

+		 

.NextStep		 

		  inc SPRITE_MOVE_POS,x

		  lda SPRITE_MOVE_POS,x

		  cmp #16

		  bne +

		 

		  lda #0

		  sta SPRITE_DIRECTION,x

		  sta SPRITE_MOVE_POS,x

+

		  rts

		 

.MoveCCWWN

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE,y

		  sta PARAM3

		  jsr .TryMoveUp

		 

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE_R,y

		  sta PARAM3

		  jsr .TryMoveRight

		  jmp .NextStep
.MoveCCWSW

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE,y

		  sta PARAM3

		  jsr .TryMoveLeft

		 

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE_R,y

		  sta PARAM3

		  jsr .TryMoveUp

		  jmp .NextStep
.MoveCCWES

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE,y

		  sta PARAM3

		  jsr .TryMoveDown

		 

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE_R,y

		  sta PARAM3

		  jsr .TryMoveLeft

		  jmp .NextStep
.MoveCCWNE

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE,y

		  sta PARAM3

		  jsr .TryMoveRight

		 

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE_R,y

		  sta PARAM3

		  jsr .TryMoveDown

		  jmp .NextStep
.MoveCWWS

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE,y

		  sta PARAM3

		  jsr .TryMoveDown

		 

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE_R,y

		  sta PARAM3

		  jsr .TryMoveRight

		  jmp .NextStep
.MoveCWNW

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE,y

		  sta PARAM3

		  jsr .TryMoveLeft

		 

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE_R,y

		  sta PARAM3

		  jsr .TryMoveDown

		  jmp .NextStep
.MoveCWEN

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE,y

		  sta PARAM3

		  jsr .TryMoveUp

		 

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE_R,y

		  sta PARAM3

		  jsr .TryMoveLeft

		  jmp .NextStep
.MoveCWSE

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE,y

		  sta PARAM3

		  jsr .TryMoveRight

		 

		  ldy SPRITE_MOVE_POS,x

		  lda PATH_CURVE_R,y

		  sta PARAM3

		  jsr .TryMoveUp

		  jmp .NextStep


.Blocked

		  lda #0

		  sta SPRITE_DIRECTION,x

		  rts
.TryMoveUp

		  beq +

		 

		  jsr ObjectMoveUpBlocking

		  beq .Blocked

		 

		  dec PARAM3

		  jmp .TryMoveUp

		 

+	

		  rts

		 

.TryMoveDown

		  beq +

		 

		  jsr ObjectMoveDownBlocking

		  beq .Blocked

		 

		  dec PARAM3

		  jmp .TryMoveDown

		 

+	

		  rts

		 

.TryMoveLeft

		  beq +

		 

		  jsr ObjectMoveLeftBlocking

		  beq .Blocked

		 

		  dec PARAM3

		  jmp .TryMoveLeft

		 

+	

		  rts

		 

.TryMoveRight

		  beq +

		 

		  jsr ObjectMoveRightBlocking

		  beq .Blocked

		 

		  dec PARAM3

		  jmp .TryMoveRight

		 

+	

		  rts

The table are simple delta updates per frame, one being the reverse of the other:
PATH_CURVE

		  !byte 0

		  !byte 0

		  !byte 1

		  !byte 0

		  !byte 0

		  !byte 1

		  !byte 0

		  !byte 1

		  !byte 0

		  !byte 1

		  !byte 1

		  !byte 1

		  !byte 1

		  !byte 1

		  !byte 1

		  !byte 1

PATH_CURVE_R

		  !byte 1

		  !byte 1

		  !byte 1

		  !byte 1

		  !byte 1

		  !byte 1

		  !byte 1

		  !byte 0

		  !byte 1

		  !byte 0

		  !byte 1

		  !byte 0

		  !byte 0

		  !byte 1

		  !byte 0

		  !byte 0

The other thing is a little fix for a bug I found with the vanishing bats for the last portal stages. The bats could appear outside the playing area. The fix are two border values for left and right which will be set to be farther inside the screen if a portal level is run:
		  ;set default
		  lda #10

		  sta SPAWN_LEFT_BORDER

		  lda #30

		  sta SPAWN_RIGHT_BORDER

 
		  ;adjust spawn border on portal level

		  lda LEVEL_CONFIG

		  and #$04

		  beq +

		 

		  lda #15

		  sta SPAWN_LEFT_BORDER

		  lda #25

		  sta SPAWN_RIGHT_BORDER

+

...and adjust the spawn code inside BehaviourBatVanishing:
		  ;position diagonal above/below player

		  lda SPRITE_CHAR_POS_X

		  cmp #SPAWN_LEFT_BORDER   ;was #10

		  bcc .SpawnOnRight
		  cmp #SPAWN_RIGHT_BORDER  ;was #30

		  bcs .SpawnOnLeft


Previous Step Next Step

Attached Files




A C64 Game - Step 96

Posted by , in C64 19 April 2013 - - - - - - · 1,314 views

And a nice little update, Richard Bayliss added sounds effects. Now there's a SFX mode, toggle in the title screen with left/right.

Attached Image

The effects are integrated in the player code as separate "songs". So we add a variable SFX_MODE and check it's value when we want to play an effect or start the music:

No music in title when SFX mode enabled:
		 ;initialise music player

		  ldx #0

		  ldy #0

		  lda SFX_MODE

		  bne +

		  lda #MUSIC_TITLE_TUNE

		  jsr MUSIC_PLAYER

Play an effect is similar:
		  lda SFX_MODE

		  beq +

		  lda #MUSIC_PICKUP

		  jsr MUSIC_PLAYER

+		 

/code]
 
To toggle sfx mode move the joystick left/right in the title screen and display its state:
 
[code]
		  ;switch through music/sfx mode

		  lda #$04

		  bit JOYSTICK_PORT_II

		  bne .NotLeftPressed

		 

		  lda LEFT_RELEASED

		  beq .LeftPressed

		 

		  lda SFX_MODE

		  eor #$01

		  sta SFX_MODE

		  jsr DisplaySfxMode

		 

		  lda SFX_MODE

		  beq +

		 

		  lda #MUSIC_PICKUP

		  jmp ++

+		 

		  lda #MUSIC_TITLE_TUNE

++		 

		  jsr MUSIC_PLAYER

		 

		 

		  lda #0

		  jmp .LeftPressed
.NotLeftPressed		 

		  lda #1

.LeftPressed		 

		  sta LEFT_RELEASED
		  lda #$08

		  bit JOYSTICK_PORT_II

		  bne .NotRightPressed

		 

		  lda RIGHT_RELEASED

		  beq .RightPressed

		 

		  lda SFX_MODE

		  eor #$01

		  sta SFX_MODE

		  jsr DisplaySfxMode

		 

		  lda SFX_MODE

		  beq +

		 

		  lda #MUSIC_PICKUP

		  jmp ++

+		 

		  lda #MUSIC_TITLE_TUNE

++		 

		  jsr MUSIC_PLAYER

		 

		  lda #0

		  jmp .RightPressed
.NotRightPressed		 

		  lda #1

.RightPressed		 

		  sta RIGHT_RELEASED


 


!zone DisplaySfxMode

DisplaySfxMode

		  lda SFX_MODE

		  bne +

		 

		  lda #<TEXT_MUSIC

		  sta ZEROPAGE_POINTER_1

		  lda #>TEXT_MUSIC

		  jmp .DisplaySfxMode

+		 

		  lda #<TEXT_SFX

		  sta ZEROPAGE_POINTER_1

		  lda #>TEXT_SFX
.DisplaySfxMode

		  sta ZEROPAGE_POINTER_1 + 1

		  lda #34

		  sta PARAM1

		  lda #24

		  sta PARAM2

		  jmp DisplayText

Due to technical limitations in the player code there are not too many sounds, but there are enough to make it worthwile.



Previous Step Next Step

Attached Files




A C64 Game - Step 95

Posted by , in C64 12 April 2013 - - - - - - · 1,264 views

Now a little update that adds a change that was long overdue: Zombies do not wake up all of a sudden, but peek out of the ground before. Now players should be able to escape if they're keeping an eye out.

Attached Image


We change the .WakeUp part of BehaviourZombie to show up, look left/right a few times and only then rise, like good zombies do:
		  ;only animate head to warn player

		  inc SPRITE_MOVE_POS,x

		  lda SPRITE_MOVE_POS,x

		  cmp #20

		  beq .ReallyWakeUp

		 

		  and #$07

		  bne ++

		 

		  ;show head

		  lda SPRITE_DIRECTION,x

		  eor #1

		  sta SPRITE_DIRECTION,x

		 

		  lda #SPRITE_ZOMBIE_COLLAPSE_R_2

		  clc

		  adc SPRITE_DIRECTION,x

		  sta SPRITE_POINTER_BASE,x

++		 

		  rts

		 

		 

.ReallyWakeUp

Also, having the spawn animation playing but then "appearing" underground is awkward the start state of zombies is now fully alive. Which is simply a change in the TYPE_START_STATE table.

That was simple now Posted Image


Previous Step Next Step

Attached Files




A C64 Game - Step 94

Posted by , in C64 05 April 2013 - - - - - - · 1,053 views

Now we have a real two player coop mode. It's not both players playing by themselves, but true teamwork. Sam needs to capture, and only then Dean can shoot enemies.

Attached Image

To makes things easier we add a new flag to check if two player mode is active (TWO_PLAYER_MODE_ACTIVE):
		  ;set two player mode active flag

		  lda #0

		  sta TWO_PLAYER_MODE_ACTIVE

		  lda GAME_MODE

		  cmp #GT_COOP

		  bne +

		  inc TWO_PLAYER_MODE_ACTIVE

+

Remove the flag if one of the player dies on his last life:
.OtherPlayerStillAlive

		  ;remove 2 player active flag

		  lda #0

		  sta TWO_PLAYER_MODE_ACTIVE

		  jsr RemoveObject

		  rts

If Sam is firing away and the enemy would be hurt, we bail out if our flag is set. The flag set means having a value of 0. So beq actually checks if it is not set:
		  ;Sam needs to keep pressed

		  jsr RedrawForceBeam

		 

		  ldy SPRITE_HELD

		  dey

		  ldx SPRITE_ACTIVE,y

		  lda IS_TYPE_ENEMY,x

		  cmp #1

		  bne .NormalHurtByForce

		 

		  ;in 2p mode?

		  lda TWO_PLAYER_MODE_ACTIVE

		  beq .NormalHurtByForce

		 

		  ;no further action

		  jmp .NoEnemyHeld

		 

		 

.NormalHurtByForce

In Deans shot routine we check if the enemy is held, if not bail out:
.EnemyHit		 

		  ;enemy hit!

		  ;is two player enemy?

		  ldy SPRITE_ACTIVE,x

		  lda IS_TYPE_ENEMY,y

		  cmp #1

		  bne .HitEnemy

		 

		  ;in 2p mode?

		  lda TWO_PLAYER_MODE_ACTIVE

		  beq .HitEnemy
 
		  ;is the player held?

		  ldy SPRITE_HELD

		  dey

		  sty PARAM1

		  cpx PARAM1

		  beq .HitEnemy

		 

		  ;enemy would be hit, but is not held

		  jmp .ShotDone

		 

.HitEnemy		 


Simple addon, but bound to get complicated Posted Image


Previous Step Next Step

Attached Files




A C64 Game - Step 93

Posted by , in C64 30 March 2013 - - - - - - · 1,111 views

An addon to make the other bosses a bit stronger against Sam. Now you need to re-grab the boss after every hit you place.
Previously Sam only had to stand there and keep fire pressed. Hardly a challenge Posted Image

Attached Image

First of all, a new enemy type is added. Normal enemies stay type 1, bosses are now type 3.

Most changes are in the FireSam routine. Add in the PLAYER_FIRE_RELEASED check:
.FireSam

		  ldy PLAYER_JOYSTICK_PORT,x

		  lda JOYSTICK_PORT_II,y

		  and #$10

		  beq +

		 

		  ;not fire pressed

		  lda #1

		  sta PLAYER_FIRE_RELEASED,x

		  jmp .SamNotFirePushed

		 

+		 

		  lda #1

		  sta PLAYER_FIRE_PRESSED_TIME,x

		 

		  stx PARAM6

		 

		  lda SPRITE_HELD

		  bne .NoFireReleasedCheck

		 

		  jsr SamUseForce

		  beq .NoEnemyHeld

		 

		  ldx CURRENT_INDEX

		  lda PLAYER_FIRE_RELEASED,x

		  bne +

		  jmp .SamNotFirePushed

+		 

		  lda #0

		  sta PLAYER_FIRE_RELEASED,x
.NoFireReleasedCheck

In the enemy hurt routine we check if a boss was hurt (by checking the type). If it is, release the enemy from the force grip:
		  ;enemy was hurt

		  ldy SPRITE_HELD

		  dey

		  lda SPRITE_ACTIVE,y

		  tay

		  lda IS_TYPE_ENEMY,y

		  cmp #3

		  ;if boss, auto-release

		  bne +

		 

		  lda #0

		  sta PLAYER_FIRE_RELEASED,x

		  jmp .SamNotFirePushed

		 

+


Have fun!



Previous Step Next Step

Attached Files




A C64 Game - Step 92

Posted by , in C64 23 March 2013 - - - - - - · 1,138 views

Poor Sam was left out again. Now he can kill the boss too.

Attached Image

Since the boss is a special beast you wouldn't want Sam just to stand there and kill him without any reaction.

We add a new variable BOSS_HELD, similar to the SPRITE_HELD value.
So if SAM hurts the enemy, and it's the boss, the boss is released from Sam's grip:
		  dec SPRITE_HP,x
		  beq .EnemyKilled

		 

		  ;enemy was hurt

		  lda BOSS_HELD

		  beq .EnemyWasHurt

		 

		  ;release if end boss

		  jmp .SamNotFirePushed

BOSS_HELD is set to 1 if Sam has the boss in his force grip. We check if the sprite caught is the last boss or one of his parts:
.EnemyHit		 

		  ;enemy hit!

		  stx SPRITE_HELD

		  ldy SPRITE_HELD

		  inc SPRITE_HELD

		 

		  lda SPRITE_ACTIVE,y

		  cmp #TYPE_BOSS7

		  beq .HoldingBoss

		  cmp #TYPE_BOSS_PART

		  beq .HoldingBoss

		  jmp .NotHoldingBoss

		 

.HoldingBoss		 

		  sty BOSS_HELD

		  inc BOSS_HELD

.NotHoldingBoss


Therefore we also need to clear the bit in case the enemy or Sam is killed:
!zone KillEnemy

KillEnemy

		  ;is the enemy currently held?

		  ldy SPRITE_HELD

		  dey

		  sty PARAM4

		  cpx PARAM4

		  bne .WasNotHeld

		 

		  lda #0

		  sta SPRITE_HELD

		  sta BOSS_HELD

Obviously the boss should not move when being caught, so in BehaviourBoss7 we add an early bail out:
.NoHitBack

		  lda BOSS_HELD

		  beq +

		 

		  rts


+


The step also adds a few bug fixes, as in the boss not auto-moving the bats he spawned (as if they were body parts).

Have fun!


Previous Step Next Step

Attached Files




A C64 Game - Step 91

Posted by , in C64 16 March 2013 - - - - - - · 1,280 views

Aaaand the torso gets to fight back too, not only sit put.


Attached Image


Time to reuse existing code again. The torso will spit out bats just like the last two bossed did. SPRITE_MODE_POS is used to stop the attacking mode and revert back to movement.

A contains the number of boss parts killed (so 4 = 2 legs plus 2 arms):
		  cmp #4

		  bne +
		  ;attack with bats

		  lda SPRITE_CHAR_POS_X,x

		  sta PARAM1

		  lda SPRITE_CHAR_POS_Y,x

		  clc

		  adc #4

		  sta PARAM2

		  inc PARAM2

		  stx PARAM10

		 

		  jsr GenerateRandomNumber

		  and #$01

		  beq .NoBatLeft
		  jsr FindEmptySpriteSlot

		  beq ++

		 

		  lda #TYPE_BAT_ATTACKING

		  sta PARAM3

		  jsr SpawnObject

		  lda #0

		  sta SPRITE_DIRECTION,x

.NoBatLeft

		  jsr GenerateRandomNumber

		  and #$01

		  beq .NoBatRight
		  jsr FindEmptySpriteSlot

		  beq ++
		  jsr SpawnObject

		  lda #1

		  sta SPRITE_DIRECTION,x

++		 

.NoBatRight

		  ldx CURRENT_INDEX

		 

		  lda SPRITE_MODE_POS,x

		  cmp #20

		  bne +++

				   

		  dec SPRITE_STATE,x

		  lda #0

		  sta SPRITE_MODE_POS,x

+++

		  rts


+

That's all there is for this step Posted Image


Previous Step Next Step

Attached Files




A C64 Game - Step 90

Posted by , in C64 09 March 2013 - - - - - - · 1,036 views

And the boss got a bit more lively (visually), it was quite stiff previously. Moving body parts and the head screams (also visually) when getting hurt.

Attached Image


The boss helper code is enhanced by a routine doing circling movements:


jsr GenerateRandomNumber
and #$03
bne .DoY

inc SPRITE_MOVE_POS,x
lda SPRITE_MOVE_POS,x
and #$0f
sta SPRITE_MOVE_POS,x
ldy SPRITE_MOVE_POS,x
lda BOSS_DELTA_TABLE_X,y
beq .DoY
sta PARAM1

bmi .Left

.Right
jsr MoveSpriteRight
dec PARAM1
bne .Right
jmp .DoY

.Left
jsr MoveSpriteLeft
inc PARAM1
bne .Left

.DoY
jsr GenerateRandomNumber
and #$03
bne .Done
inc SPRITE_MOVE_POS_Y,x
lda SPRITE_MOVE_POS_Y,x
and #$0f
sta SPRITE_MOVE_POS_Y,x
ldy SPRITE_MOVE_POS_Y,x
lda BOSS_DELTA_TABLE_Y,y
beq .Done
sta PARAM1

bmi .Up

.Down
jsr MoveSpriteDown
dec PARAM1
bne .Down
jmp .Done

.Up
jsr MoveSpriteUp
inc PARAM1
bne .Up

.Done
rts


Plus a rather simple swinging table:
BOSS_DELTA_TABLE_X
!byte 0, 1, 0, 1, 1, 0, 1, 0
BOSS_DELTA_TABLE_Y
!byte 0, $ff, 0, $ff, $ff, 0, $ff, 0
!byte 0, 1, 0, 1, 1, 0, 1, 0


To make the boss' head scream we adjust its hurt routine:

;------------------------------------------------------------
;hit behaviour getting hurt
;------------------------------------------------------------
!zone HitBehaviourBoss7
HitBehaviourBoss7
lda #SPRITE_BOSS_HEAD_HURT
sta SPRITE_POINTER_BASE,x
jmp HitBehaviourHurt



Previous Step Next Step

Attached Files








December 2016 »

S M T W T F S
    123
456789 10
11121314151617
18192021222324
25262728293031