Jump to content

  • Log In with Google      Sign In   
  • Create Account

New Old Things

Adventurous Adventure

Posted by , 28 July 2015 - - - - - - · 1,202 views
C64 adventure
I'm currently attending yet another C64 game creation competition: Forum64.de's Adventure Competition 2015.

I always wondered how the guys/gals at Lucasfilm Games managed to cram Maniac Mansion and Zak McKracken onto a few small disks. So this competition was the perfect opportunity to test drive my ideas.


How they did it

Fortunately today the internet provides all kind of technical info at my finger tips. So it's no problem finding some of the old programmers spilling their beans. They actually created a Lisp like language that allowed them scripting even with a kind of multi threading. Incredible! Obviously the script was compiled to byte code which barely fit into memory. So that's the way I chose to follow.
Fun link of the day: Ron Gilbert on Scumm (http://grumpygamer.com/scumm_notes)


How I faked it

Tables. Tables with pointers to more tables. All verbs are represented by indices; so are items and static objects (items on screen)
Every item/object has a list of verbs to scripts. Item 5 with verb 7? Run this script. No entry for object 14 and verb 9? Show the dreaded "That doesn't work."

And there's action areas with type "room exit" or "script trigger". Room exits are handled directly while script triggers start their attached script.


Where's the beef?

It's in the script! All puzzles are handled by various script actions. A script action can set/clear flags, run a different script depending on a flag state. Characters can be "walked", get or lose an item, branch if a certain combination of items/objects is used, if the controlled character is anybody specific, etc.

This script together with rooms made up of elements (of which some may depend on certain flag states) makes for a quite small memory footprint. At the current state there's 65 flags, 39 rooms, 55 static objects and 18 items making up a total of 30355 bytes (graphics included).

Since that's all pretty abstract, here's a few excerpts.


Action table for a static object (badge reader in elevator)
SOA_BADGE_BUTTON_IN_LIFT
          !byte PA_EXAMINE, <CS_EXAMINE_BADGE_BUTTON, >CS_EXAMINE_BADGE_BUTTON
          !byte PA_LAST_ENTRY_FLAG | PA_USE, <CS_USE_BADGE_BUTTON_IN_LIFT, >CS_USE_BADGE_BUTTON_IN_LIFT
Starts a sequence when a button is used in the elevator, referenced in the table above

CS_USE_BADGE_BUTTON_IN_LIFT
          !byte CSA_SET_HAPPEN,2
          !byte CSA_REDRAW_SCREEN
          !byte CSA_WAIT,50
          !byte CSA_IF_HAPPEN_SET,4,<CS_ACCIDENT,>CS_ACCIDENT
          !byte CSA_CLEAR_HAPPEN,2
          !byte CSA_TOGGLE_HAPPEN,3
          !byte CSA_LAST_ENTRY_FLAG | CSA_REDRAW_SCREEN</pre>



The Week of Awesome II - Post Mortem

Posted by , 02 October 2014 - - - - - - · 1,440 views

Post Mortem to "Building Blocks"

Attached Image

So I entered the Week of Awesome II competition last week. I simply love this tight deadline competitions. And I couldn't help it again.
The theme came up as "The Toys are alive". Yay.

Usually I don't jump into coding right away to think about the game to be. And as usual I always go for the nearest idea. It's very rare that I try to outwit the theme and do something really creative. And so it was decided, a simple Lemmings style game, with tin soldiers walking about building blocks.

The Good

Level design

The common start is to think of how to keep the level in memory. The base of the level is tile based. I did think of bigger blocks right away, and this worked out fine after a few tries. This led me to the next point pretty fast, the


Level editor

Attached Image

Esp. for these competitions, when I go level based, I need a way to churn out levels very fast. Since this competition lasted for a week I went out of my way to make a decent editor. It was a bit more work in the beginning, but helped me tremendously during the last two days to play with level design and add some more stages. I wouldn't have managed 10 stages without it.


Base Library

For Ludum Dare we have to provide the full source. This makes me hesitate to actually use my home grown engine and I work with a very simple cut down framework. For the GameDev compo I can use my full framework and it does help quite a lot. I've got game assets, GUI and game states from the start, and especially, I'm comfortable with it.


Generated Music

Previous to the last Ludum Dare I was on the search for music generators once more. I encountered cgMusic, which made some really nice songs. It's a bit piano centric, and I feel that some song parts to repeat, but it was good enough for me. Adding a midi to ogg converter I could generate songs in a few minutes.


Early Preview

As suggested I put up an early version. And it was a good idea. I had some very valuable feedback, as there were a few things I didn't even think of. I think I did add all suggestions.


The Bad

Collision system

The sloped tiles were made out as a first issue to tackle. For some reason I thought using polygons would work out nice. Since I have a math library with polygon collision code I wrote polygon creators for the different blocks. Collision with a unit's bounds rectangle (=polygon) worked very nicely.
However problems appeared when I actually implemented the movement system. I had my units move in pixel units. Obviously mixing pixel based movement with "exact" mathematics (think of the slope of a diagonal block) does not work out. Units fell one pixel, ended up inside the block polygon and were stuck.

I ended up adding a simple hack: return the pixel based height of a tile depending on the x position in the tile. Ugly, but worked.


Ugly bug

When I had the first preview out someone mentioned that the game crashes on exit. I despise things like that (Gamemaker games, I'm looking at you). In the end the real bug was a bug in my level block handling. I managed to access out of bounds memory and as usual this crashed at a totally unrelated position. I had the luck that some code changes led to the bug appearing right away after the wrong access. It took a few seconds to fix, but I noticed that due to that bug some stages had broken data saved.

I added a simple hack: On loading levels I auto fixed these broken parts.



The Ugly

Windows 7 auto volume adjustment

Windows 7 does some automatic volume adjusting. If there's only very low volume music, it turns it up. But if the music suddenly gets louder it gets adjusted down. I do understand the reasoning behind this feature, but it doesn't lend itself to gaming too well. I wish there was a way for my game to tell Windows to not mess with my volume.

This feature is really really annoying. I'm not entirely sure how to work around it beside starting to play a loud sound effect at the start.



TL;DR

As final thought I'm pretty happy how the game turned out. It's not my best, but also not my worst. I wish I had more stages, as the first 5 are simple tutorials. At least the last two stages are a bit of a puzzle though.


Soooo, when does the next compo start?


A C64 Game - Final Step

Posted by , in C64 17 May 2013 - - - - - - · 2,479 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 99

Posted by , 10 May 2013 - - - - - - · 1,606 views

And of course lots of little bugs were found and fixed Posted Image

-Live number display was off on a new level.
-Beams would sometimes not be removed on the final boss
-Disable screen output when saving scores (IRQs go nuts if using kernal save routine)
-Cleaned up "extro" text


Have fun!



Previous Step Next 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,112 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








December 2016 »

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