A C64 Game - Step 38
Now this is one huge step that took me a week to implement: A second player. Can't have Supernatural with only Dean, now there's Sam as well.
In the title screen, press up to toggle game modes (single Dean, single Sam, coop). Note that Sam does not use a shot gun, he uses his dark powers.

We start by adding the game modes:
;game mode types
GT_SINGLE_PLAYER_DEAN = 0
GT_SINGLE_PLAYER_SAM = 1
GT_COOP = 2
Of course the current mode needs to be displayed in the main menu. We use GAME_MODE as index into a text mode description table.
ldx GAME_MODE
lda TEXT_GAME_MODE_LO,x
sta ZEROPAGE_POINTER_1
lda TEXT_GAME_MODE_HI,x
sta ZEROPAGE_POINTER_1 + 1
lda #11
sta PARAM1
lda #21
sta PARAM2
jsr DisplayText
...and let the player toggle through the game modes with a joystick up movement:
..and the joystick port a player uses. This snippet makes sure that for single player modes the used joystick port is port 2.
;settings per game mode
;default ports
lda #0
sta PLAYER_JOYSTICK_PORT
lda #1
sta PLAYER_JOYSTICK_PORT + 1
lda GAME_MODE
cmp #GT_SINGLE_PLAYER_SAM
bne .NoPortChange
lda #0
sta PLAYER_JOYSTICK_PORT + 1
.NoPortChange
A lot of changes from Dean to Same are quite simple to implement. Reuse Dean's code, but add an index to the sprite tables. Since Dean is always in slot 0, the ",x" was omitted to speed code up. Now we just put ,x for every sprite table access (Sam is always in slot 1), and the basic code just works.
Some pieces had to be written anew from scratch; for example the fire code for Sam. Sam does not use a shotgun, he uses his demonic forces instead. Sam needs to look at an enemy, and keep fire pressed. Doing that a enemy will get frozen and take damage. However Sam cannot move during that phase.
.FireSam
ldy PLAYER_JOYSTICK_PORT,x
lda JOYSTICK_PORT_II,y
and #$10
bne .SamNotFirePushed
lda #1
sta PLAYER_FIRE_PRESSED_TIME,x
stx PARAM6
jsr SamUseForce
beq .NoEnemyHeld
;Sam needs to keep pressed
inc PLAYER_SHOT_PAUSE,x
lda PLAYER_SHOT_PAUSE,x
cmp #40
beq .EnemyHurtBySam
ldy SPRITE_HELD
dey
lda #2
sta VIC_SPRITE_COLOR,y
.NoEnemyHeld
.EnemyWasHurt
;restore sprite index
ldx PARAM6
jmp .NotFirePushed
.EnemyHurtBySam
lda #0
sta PLAYER_SHOT_PAUSE,x
ldx SPRITE_HELD
dex
lda #0
sta VIC_SPRITE_COLOR,x
dec SPRITE_HP,x
bne .EnemyWasHurt
.EnemyKilledBySam
lda #5
jsr IncreaseScore
ldx SPRITE_HELD
dex
jsr KillEnemy
ldx PARAM6
lda #0
sta SPRITE_HELD
jmp .NotFirePushed
.SamNotFirePushed
lda #0
sta SPRITE_HELD
sta PLAYER_SHOT_PAUSE,x
sta PLAYER_FIRE_PRESSED_TIME,x
jmp .NotFirePushed
step38.zip