Jump to content
IGNORED

My Atari Lynx Programming Journal


OldAtAtari

Recommended Posts

I wanted to see what the assembly command INC does, so I looked it up. I already knew that INA would increment the accumulator. So INC will increment the value stored at a specific memory location, in this case, the memory referred to as "_score". So score++ looks like it loads the value from memory into the accumulator, increments that, and then stores it back into memory (three steps) whereas ++score simply increments the score where it sits in memory (one step). I had no idea! Learn something every day.

I read about INC in this document (about the 6502, not the 65C02, but still looks really good): https://www.atariarchives.org/alp/appendix_1.php

 

And that's part of a book found in the Atari Archives: https://www.atariarchives.org/sitemap.php


But here's a nice document regarding the 65C02: http://www.retroisle.com/general/6502ref.html

Edited by OldAtAtari
  • Like 2
Link to comment
Share on other sites

Hey, guys. I'm struggling with collisions. I tried following the example here https://atarilynxdeveloper.wordpress.com/2012/12/09/programming-tutorial-part-10collisions/, but I couldn't get it to compile. That lesson is from 2012, so I wonder if maybe things have changed since then.

I tried copying code from various games I found around the web, but they all seem to handle collisions in a different way, and I couldn't get any of the methods to work.

 

The game.c example in the template doesn't seem set up for collisions, and I'm trying to modify it for that. But I just can't get it to compile once I add in any tgi collision code. I feel like there's some documentation that I'm missing somewhere. I was looking at the tgi documentation here: https://cc65.github.io/doc/tgi.html, but it doesn't seem to talk about collisions. A search for tgi_setcollisiondetection on that page doesn't turn up anything. And even just adding that one line to my code causes compilation warnings and errors:

game.c(6): Warning: Implicit `int' is an obsolete feature
game.c(6): Error: `)' expected
game.c(6): Error: Conflicting types for `tgi_ioctl'
game.c(6): Error: `{' expected
game.c(6): Warning: Statement has no effect
game.c(6): Error: `;' expected
game.c(6): Error: Expression expected
game.c(6): Warning: Statement has no effect

 

I'm completely on the wrong track here. Does anyone have any advice or pointers to the most recent documentation of the tgi collision code?

Thanks.

Link to comment
Share on other sites

16 minutes ago, OldAtAtari said:

Hey, guys. I'm struggling with collisions. I tried following the example here https://atarilynxdeveloper.wordpress.com/2012/12/09/programming-tutorial-part-10collisions/, but I couldn't get it to compile. That lesson is from 2012, so I wonder if maybe things have changed since then.

I tried copying code from various games I found around the web, but they all seem to handle collisions in a different way, and I couldn't get any of the methods to work.

 

The game.c example in the template doesn't seem set up for collisions, and I'm trying to modify it for that. But I just can't get it to compile once I add in any tgi collision code. I feel like there's some documentation that I'm missing somewhere. I was looking at the tgi documentation here: https://cc65.github.io/doc/tgi.html, but it doesn't seem to talk about collisions. A search for tgi_setcollisiondetection on that page doesn't turn up anything. And even just adding that one line to my code causes compilation warnings and errors:

game.c(6): Warning: Implicit `int' is an obsolete feature
game.c(6): Error: `)' expected
game.c(6): Error: Conflicting types for `tgi_ioctl'
game.c(6): Error: `{' expected
game.c(6): Warning: Statement has no effect
game.c(6): Error: `;' expected
game.c(6): Error: Expression expected
game.c(6): Warning: Statement has no effect

 

I'm completely on the wrong track here. Does anyone have any advice or pointers to the most recent documentation of the tgi collision code?

Thanks.

You just have a typo on line 6.

 

My guess is that you are missing an include file like

#include <tgi.h>
#include <lynx.h>

 

You may also get this error if you leave out the type in your code. Like:

 

count;

 

instead of

 

unsigned char count;

 

  • Like 1
Link to comment
Share on other sites

Thanks, Karri! I didn't see any typos, so I just deleted all my collision code and redid it, following the example at https://atarilynxdeveloper.wordpress.com/2012/12/09/programming-tutorial-part-10collisions/.

 

Here's my code and my errors:

 

#include <lynx.h>
#include <conio.h>
#include <joystick.h>
#include <tgi.h>
#include <stdlib.h>

int tgi_setcollisiondetection(1);

unsigned char checkInput(void);
extern unsigned char reset; 

void lynx_snd_init ();
void lynx_snd_pause ();
void lynx_snd_continue ();
void __fastcall__ lynx_snd_play (unsigned char channel, unsigned char *music);
void lynx_snd_stop ();

typedef struct {
    unsigned char *music0;
    unsigned char *music1;
    unsigned char *music2;
    unsigned char *music3;
} song_t;

extern song_t musicptr;

extern unsigned char tree[];

extern unsigned char dude[];

typedef struct SCB_REHV_PAL {
  unsigned char sprctl0;
  unsigned char sprctl1;
  unsigned char sprcoll;
  char *next;
  unsigned char *data;
  signed int hpos;
  signed int vpos;
  unsigned int hsize;
  unsigned int vsize;
  unsigned char penpal[8];
} SCB_REHV_PAL;

typedef struct {
  byte depository;
  SCB_REHV_PAL scb;
} sprite_collideable;

sprite_collideable Stree = {
    0x0 {
    BPP_4 | TYPE_NORMAL,
    0x10, 0x02,
    0,
    tree,
    0, 0,
    0x0100, 0x100,
    {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}
    }
};

sprite_collideable Sdude = {
    0x0 {
    BPP_4 | TYPE_NORMAL,
    0x10, 0x01,
    0,
    dude,
    0, 0,
    0x0100, 0x100,
    {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}
    }
};



/* ----------------- Road -------------------- */
unsigned char pixel[] = { 3, 0x84, 0, 0 };

static SCB_REHVST_PAL Spixel = {
    BPP_1 | TYPE_BACKGROUND,
    0x30, 0x20,
    0,
    pixel,
    50, 50,
    0x2800, 0x800,
    0x00, 0x100,
    {0x68}
};

void polygon(int x1, int y1, int w1, int x2, int y2, int w2, unsigned char color)
{
    Spixel.hpos = x1;
    Spixel.vpos = y1;
    Spixel.hsize = w1 << 8;
    Spixel.vsize = (y2 - y1 + 1) << 8;
    Spixel.tilt = (x2 - x1) * 256 / (y2 - y1);
    Spixel.stretch = (w2 - w1) * 256 / (y2 - y1);
    Spixel.penpal[0] = color;
    tgi_sprite(&Spixel);
}

void
drawsegment(int lanes, int x1, int y1, int w1, int x2, int y2, int w2)
{
    int l1;
    int l2;
    int r1;
    int r2;
    int lanew1;
    int lanew2;
    int lanex1;
    int lanex2;
    int lane;
    l1 = w1 >> (lanes + 2);
    l2 = w2 >> (lanes + 2);
    r1 = w1 >> (lanes + 1);
    r2 = w2 >> (lanes + 1);
    // Draw left yellow rumble line
    polygon(x1 - w1 - r1 + 1, y1, r1, x2 - w2 - r2 + 1, y2, r2, COLOR_YELLOW);
    // Draw road
    polygon(x1 - w1, y1, w1, x2 - w2, y2, w2, COLOR_GREY);
    lanew1 = w1 * 2 / lanes;
    lanew2 = w2 * 2 / lanes;
    lanex1 = x1 - w1 + lanew1;
    lanex2 = x2 - w2 + lanew2;
    for (lane = 1; lane < lanes; lanex1 += lanew1, lanex2 += lanew2, lane++) {
        polygon(lanex1 - l1 / 2, y1, l1, lanex2 - l2 / 2, y2, l2, COLOR_WHITE);
    }
    polygon(x1 - 1 + l1 / 2, y1, w1, x2 - 1 + l2 / 2, y2, w2, COLOR_GREY);
    // Draw right yellow rumble line
    polygon(x1 + w1, y1, r1, x2 + w2, y2, r2, COLOR_YELLOW);
    // Draw right cleanup
    polygon(x1 + w1 + r1 - 1, y1, r1, x2 + w2 + r2 - 1, y2, r2, COLOR_DARKGREY);
}

void draw_tree (int x, int y, int scale)
{
    Stree.vpos = y;
    Stree.hpos = x + 0 * 256 / 256;
    Stree.vsize = 33;
    Stree.hsize = 33;
    tgi_sprite(&Stree.scb);
    itoa(Stree.depository, text 10)
}

void draw_dude (int dx, int dy, int scale)
{
    Sdude.vpos = dy;
    Sdude.hpos = dx + 0 * 256 / 256;
    Sdude.vsize = 35;
    Sdude.hsize = 35;
    tgi_sprite(&Sdude.scb);
    itoa(Sdude.depository, text 10)
}

void
drawscreen(int x, int y, int dx, int dy, int turn, int score)
{
    int y2;
    int scale1, scale2;
    char textScore[20];
    itoa(score, textScore, 10);
    scale1 = 3 * y + 8;
    y2 = y + ((20 * scale1) >> 8);
    scale2 = 3 * y2 + 8;
    tgi_setcolor(COLOR_DARKGREY);
    tgi_bar(0, 0, 159, 101);
    tgi_setcolor(COLOR_YELLOW);
    tgi_outtextxy(120, 10, textScore);	
//    drawsegment(2, x, y, (80 * scale1) >> 8, x + turn, y2, (80 * scale2) >> 8);
    draw_tree(x, y, scale2);
    draw_dude(dx, dy, scale2);    
}

void game()
{
    int x = 160;
    int dx = 10;
    int y = 80;
    int dy = 80;
    int turn = 0;
	int jump = 0;
	int jumpUp = 25;
	int jumpDown = 25;
	int score = 0;

    while (!reset) { // never use while(1). Every loop needs to be halted by the reset event
        if (!tgi_busy()) {
            unsigned char joy;
 
 if (Stree.depository > 0)
{
  tgi_outtextxy(0, 90, "TREE!");
  itoa(Stree.depository, text, 10);
  tgi_outtextxy(50, 90, text);
}
 
 if (Sdude.depository > 0)
{
  tgi_outtextxy(0, 90, "TREE!");
  itoa(Sdude.depository, text, 10);
  tgi_outtextxy(50, 90, text);
} 
 
            
// Instead of calling joy_read() directly, use checkInput() defined in resident.c . 
// It will return the joy state, but will handle pause and reset events too
            
//            joy = joy_read(JOY_1);
            joy = checkInput();
            if (JOY_BTN_UP(joy)) {
            }
            if (JOY_BTN_DOWN(joy)) {
            }
            if (JOY_BTN_RIGHT(joy)) {
                dx++;
                if (dx > 150) dx = 150;
            }
            if (JOY_BTN_LEFT(joy)) {
                dx--;
                if (dx < 10) dx = 10;
            }
            if (JOY_BTN_FIRE(joy) && jump == 0) {
                jump=1;
		lynx_snd_play(1, musicptr.music3);
		score++;
            }
	    if (jump == 1 && jumpUp != 0)
	    	{
	    	dy = dy - 2;
		jumpUp--;
		}
	    if (jump == 1 && jumpUp == 0 && jumpDown != 0)
	    	{
		dy = dy + 2;
		jumpDown--;
		}
	    if (jumpDown == 0)
	    	{
		jump = 0;
		jumpUp = 25;
		jumpDown = 25;
		}
            if (JOY_BTN_FIRE2(joy)) {
                turn--;
            }
	    if (x == 0)
	    	{
		x=160;
	    	}
	    x--;
            drawscreen(x, y, dx, dy, turn, score);
            tgi_updatedisplay();
        }
    }
}

 

image.thumb.png.78aa8651b08adb56f22f05b29b2d0a14.png

 

Oh, and I just realized I hadn't implemented the changes from 42bs (pre/post increment). I still need to do that.

Thanks.

Edited by OldAtAtari
Link to comment
Share on other sites

5 minutes ago, 42bs said:

A prototype shall not have a parameter (line 7).

Oh! I bet you're talking about my line

int tgi_setcollisiondetection(1);

That was a goof. Sorry! I had been experimenting and forgot to take that out before I copied and pasted. Here is the actual code

 

#include <lynx.h>
#include <conio.h>
#include <joystick.h>
#include <tgi.h>
#include <stdlib.h>

tgi_setcollisiondetection(1);

unsigned char checkInput(void);
extern unsigned char reset; 

void lynx_snd_init ();
void lynx_snd_pause ();
void lynx_snd_continue ();
void __fastcall__ lynx_snd_play (unsigned char channel, unsigned char *music);
void lynx_snd_stop ();

typedef struct {
    unsigned char *music0;
    unsigned char *music1;
    unsigned char *music2;
    unsigned char *music3;
} song_t;

extern song_t musicptr;

extern unsigned char tree[];

extern unsigned char dude[];

typedef struct SCB_REHV_PAL {
  unsigned char sprctl0;
  unsigned char sprctl1;
  unsigned char sprcoll;
  char *next;
  unsigned char *data;
  signed int hpos;
  signed int vpos;
  unsigned int hsize;
  unsigned int vsize;
  unsigned char penpal[8];
} SCB_REHV_PAL;

typedef struct {
  byte depository;
  SCB_REHV_PAL scb;
} sprite_collideable;

sprite_collideable Stree = {
    0x0 {
    BPP_4 | TYPE_NORMAL,
    0x10, 0x02,
    0,
    tree,
    0, 0,
    0x0100, 0x100,
    {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}
    }
};

sprite_collideable Sdude = {
    0x0 {
    BPP_4 | TYPE_NORMAL,
    0x10, 0x01,
    0,
    dude,
    0, 0,
    0x0100, 0x100,
    {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}
    }
};



/* ----------------- Road -------------------- */
unsigned char pixel[] = { 3, 0x84, 0, 0 };

static SCB_REHVST_PAL Spixel = {
    BPP_1 | TYPE_BACKGROUND,
    0x30, 0x20,
    0,
    pixel,
    50, 50,
    0x2800, 0x800,
    0x00, 0x100,
    {0x68}
};

void polygon(int x1, int y1, int w1, int x2, int y2, int w2, unsigned char color)
{
    Spixel.hpos = x1;
    Spixel.vpos = y1;
    Spixel.hsize = w1 << 8;
    Spixel.vsize = (y2 - y1 + 1) << 8;
    Spixel.tilt = (x2 - x1) * 256 / (y2 - y1);
    Spixel.stretch = (w2 - w1) * 256 / (y2 - y1);
    Spixel.penpal[0] = color;
    tgi_sprite(&Spixel);
}

void
drawsegment(int lanes, int x1, int y1, int w1, int x2, int y2, int w2)
{
    int l1;
    int l2;
    int r1;
    int r2;
    int lanew1;
    int lanew2;
    int lanex1;
    int lanex2;
    int lane;
    l1 = w1 >> (lanes + 2);
    l2 = w2 >> (lanes + 2);
    r1 = w1 >> (lanes + 1);
    r2 = w2 >> (lanes + 1);
    // Draw left yellow rumble line
    polygon(x1 - w1 - r1 + 1, y1, r1, x2 - w2 - r2 + 1, y2, r2, COLOR_YELLOW);
    // Draw road
    polygon(x1 - w1, y1, w1, x2 - w2, y2, w2, COLOR_GREY);
    lanew1 = w1 * 2 / lanes;
    lanew2 = w2 * 2 / lanes;
    lanex1 = x1 - w1 + lanew1;
    lanex2 = x2 - w2 + lanew2;
    for (lane = 1; lane < lanes; lanex1 += lanew1, lanex2 += lanew2, lane++) {
        polygon(lanex1 - l1 / 2, y1, l1, lanex2 - l2 / 2, y2, l2, COLOR_WHITE);
    }
    polygon(x1 - 1 + l1 / 2, y1, w1, x2 - 1 + l2 / 2, y2, w2, COLOR_GREY);
    // Draw right yellow rumble line
    polygon(x1 + w1, y1, r1, x2 + w2, y2, r2, COLOR_YELLOW);
    // Draw right cleanup
    polygon(x1 + w1 + r1 - 1, y1, r1, x2 + w2 + r2 - 1, y2, r2, COLOR_DARKGREY);
}

void draw_tree (int x, int y, int scale)
{
    Stree.vpos = y;
    Stree.hpos = x + 0 * 256 / 256;
    Stree.vsize = 33;
    Stree.hsize = 33;
    tgi_sprite(&Stree.scb);
    itoa(Stree.depository, text 10)
}

void draw_dude (int dx, int dy, int scale)
{
    Sdude.vpos = dy;
    Sdude.hpos = dx + 0 * 256 / 256;
    Sdude.vsize = 35;
    Sdude.hsize = 35;
    tgi_sprite(&Sdude.scb);
    itoa(Sdude.depository, text 10)
}

void
drawscreen(int x, int y, int dx, int dy, int turn, int score)
{
    int y2;
    int scale1, scale2;
    char textScore[20];
    itoa(score, textScore, 10);
    scale1 = 3 * y + 8;
    y2 = y + ((20 * scale1) >> 8);
    scale2 = 3 * y2 + 8;
    tgi_setcolor(COLOR_DARKGREY);
    tgi_bar(0, 0, 159, 101);
    tgi_setcolor(COLOR_YELLOW);
    tgi_outtextxy(120, 10, textScore);	
//    drawsegment(2, x, y, (80 * scale1) >> 8, x + turn, y2, (80 * scale2) >> 8);
    draw_tree(x, y, scale2);
    draw_dude(dx, dy, scale2);    
}

void game()
{
    int x = 160;
    int dx = 10;
    int y = 80;
    int dy = 80;
    int turn = 0;
	int jump = 0;
	int jumpUp = 25;
	int jumpDown = 25;
	int score = 0;

    while (!reset) { // never use while(1). Every loop needs to be halted by the reset event
        if (!tgi_busy()) {
            unsigned char joy;
 
 if (Stree.depository > 0)
{
  tgi_outtextxy(0, 90, "TREE!");
  itoa(Stree.depository, text, 10);
  tgi_outtextxy(50, 90, text);
}
 
 if (Sdude.depository > 0)
{
  tgi_outtextxy(0, 90, "TREE!");
  itoa(Sdude.depository, text, 10);
  tgi_outtextxy(50, 90, text);
} 
 
            
// Instead of calling joy_read() directly, use checkInput() defined in resident.c . 
// It will return the joy state, but will handle pause and reset events too
            
//            joy = joy_read(JOY_1);
            joy = checkInput();
            if (JOY_BTN_UP(joy)) {
            }
            if (JOY_BTN_DOWN(joy)) {
            }
            if (JOY_BTN_RIGHT(joy)) {
                dx++;
                if (dx > 150) dx = 150;
            }
            if (JOY_BTN_LEFT(joy)) {
                dx--;
                if (dx < 10) dx = 10;
            }
            if (JOY_BTN_FIRE(joy) && jump == 0) {
                jump=1;
		lynx_snd_play(1, musicptr.music3);
		score++;
            }
	    if (jump == 1 && jumpUp != 0)
	    	{
	    	dy = dy - 2;
		jumpUp--;
		}
	    if (jump == 1 && jumpUp == 0 && jumpDown != 0)
	    	{
		dy = dy + 2;
		jumpDown--;
		}
	    if (jumpDown == 0)
	    	{
		jump = 0;
		jumpUp = 25;
		jumpDown = 25;
		}
            if (JOY_BTN_FIRE2(joy)) {
                turn--;
            }
	    if (x == 0)
	    	{
		x=160;
	    	}
	    x--;
            drawscreen(x, y, dx, dy, turn, score);
            tgi_updatedisplay();
        }
    }
}

 

 

 

Link to comment
Share on other sites

Yep. Just delete line 7 completely. This routine is already declared in lynx.h.

 

#define tgi_setcollisiondetection(active) tgi_ioctl(5, (void*)(active))
 

In your main code you can use it like

 

... do something

     tgi_setcollisiondetection(1);

... do something

 

But if you really want to use collision detection then you must use the correct cfg setup also.

 

This defines the three screen buffers in memory correctly for the code to work.

 

In the template you need to edit the cart/lynx.cfg to use the correct config setup.

 

    # Screen is just below the top vectors
    __SCREEN_SIZE__: value = 8160, type = export;
    __MEMORY_SCREEN1__: value = __MEMORY_TOP__ - __SCREEN_SIZE__, type = export;
    __MEMORY_SCREEN0__: value = __MEMORY_SCREEN1__ - __SCREEN_SIZE__, type = export;
    __MEMORY_COLLISIONSCREEN__: value = __MEMORY_SCREEN0__ - __SCREEN_SIZE__, type = export;

 

And then use  __MEMORY_COLLISIONSCREEN__ instead of __MEMORY_SCREEN0__ in defining the lower memory areas in lynx.cfg

 

  • Like 1
Link to comment
Share on other sites

8 minutes ago, karri said:

Yep. Just delete line 7 completely. This routine is already declared in lynx.h.

 

#define tgi_setcollisiondetection(active) tgi_ioctl(5, (void*)(active))
 

In your main code you can use it like

 

... do something

     tgi_setcollisiondetection(1);

... do something

 

But if you really want to use collision detection then you must use the correct cfg setup also.

 

This defines the three screen buffers in memory correctly for the code to work.

 

In the template you need to edit the cart/Makefile to use the correct config setup.

 

    # Screen is just below the top vectors
    __SCREEN_SIZE__: value = 8160, type = export;
    __MEMORY_SCREEN1__: value = __MEMORY_TOP__ - __SCREEN_SIZE__, type = export;
    __MEMORY_SCREEN0__: value = __MEMORY_SCREEN1__ - __SCREEN_SIZE__, type = export;
    __MEMORY_COLLISIONSCREEN__: value = __MEMORY_SCREEN0__ - __SCREEN_SIZE__, type = export;

 

And then use  __MEMORY_COLLISIONSCREEN__ instead of __MEMORY_SCREEN0__ in defining the lower memory areas in lynx.cfg

 

Such as this?

 

    # Screen is just below the top vectors
    __SCREEN_SIZE__: value = 8160, type = export;
    __MEMORY_SCREEN1__: value = __MEMORY_TOP__ - __SCREEN_SIZE__, type = export;
    __MEMORY_SCREEN0__: value = __MEMORY_SCREEN1__ - __SCREEN_SIZE__, type = export;
    __MEMORY_COLLISIONSCREEN__: value = __MEMORY_SCREEN0__ - __SCREEN_SIZE__, type = export; 
    # Under the screen we put the C-stack
    __STACKSIZE__: value = $800, type = export; # 2K stack
    __MEMORY_STACK__: value = __MEMORY_COLLISIONSCREEN__ - __STACKSIZE__, type = export;

 

Link to comment
Share on other sites

Ok, so edited that .cfg file, and I removed the tgi_setcollions line entirely.

 

Which leaves me with new errors, regarding multiple definitions for SCB_REHV_PAL. But I think that's progress. I'll go back to the example and see if there's something I missed. Thanks.

 

image.thumb.png.d2e278a43b7348152503296278353dc5.png

Link to comment
Share on other sites

Well, it is not progress to have too many errors.

kesäteatterissa

Take the 1st line that has the error and fix it. There is no point in reading the rest of the error list as the whole program is wrong after the 1st offending line.

 

You also need to remove the complete definition of

 

typedef struct SCB_REHV_PAL {
  unsigned char sprctl0;
  unsigned char sprctl1;
  unsigned char sprcoll;
  char *next;
  unsigned char *data;
  signed int hpos;
  signed int vpos;
  unsigned int hsize;
  unsigned int vsize;
  unsigned char penpal[8];
} SCB_REHV_PAL;

 

It has been defined in the lynx.h already. It appears that the collision example you picked was compiled with an older version of the compiler.

 

  • Like 1
Link to comment
Share on other sites

12 minutes ago, karri said:

It appears that the collision example you picked was compiled with an older version of the compiler.

 

 

Yes, I thought that might be the case. That particular tutorial seems to have lots of good information, but every time I've tried to borrow code from it, it's been out of date.

 

And I borrowed a lot from it. The whole collisions procedure. I'm tempted to delete every bit of it.

 

So that being the case, if I revert to my non-collisions game.c, which is just a modified game.c from the template, can you tell me what I do need to add to game.c to make the collisions work? I've adjusted the *.cfg as you suggested before, but I know I need to do something in my game.c code enable collisions, or at least to check for collisions. And at this point, I don't trust any examples or any documentation out there.

 

I know I'm asking a lot. Thank you for your help.

Link to comment
Share on other sites

@OldAtAtari I was meaning to write earlier that LX:net's programming tutorials are the best place to read and learn about Lynx theory, unfortunately the code examples themselves there don't mix well with Karri's cc65 or the template. I mean there's only small differences in the code details, so you can use the knowledge there, but you need to look how it's done in the template and adapt it for the small syntax differences in Karri's cc65. But most of the Lynxes functionality that you'd need for a game is already covered in the template.

Also really important to save you sweat and tears, there's half a thread here why it's maybe not the best idea to use Lynx hardware collission.

 

Edited by Turbo Laser Lynx
  • Like 1
Link to comment
Share on other sites

2 minutes ago, Turbo Laser Lynx said:

@OldAtAtari I was meaning to write earlier that LX:net's programming tutorials are the best place to read and learn about Lynx theory, unfortunately the code examples themselves there don't mix well with Karri's cc65 or the template. I mean there's only small differences in the code details, so you can use the knowledge there, but you need to look how it's done in the template and adapt it for the small syntax differences in Karri's cc65.

Also really important to save you sweat and tears, there's half a thread here why it's maybe not the best idea to use Lynx hardware collission.

Oh my! If that's the case, then what do you do? Just compare pixels to determine overlaps?

 

Link to comment
Share on other sites

The collision stuff in the ping-pong example should work.

 

But most people (including me) prefer to do the collision in C-code as it is more efficient than wasting a whole screen of bytes just for a collision buffer.

 

In the recent Titan game I check if my drone hits an object. For that purpose I wrote a small routine.


It is a bit more complex due to my desire to use shaped sprites and more fields in the sprite struct.

static unsigned char collide(sprite_t *Tgt) {
    signed char upoff = 10 - 1;
    signed char downoff = -10 + 1;
    signed char tgthead = -15;
    signed char tgttail = 15;
    signed char droneupoff = 11;
    signed char dronedownoff = 11 - 26;
    signed char dronehead = -26;
    signed char dronetail = 31 - 26;

    if (Tgt->data == ball || Tgt->data == ballhit) {
        tgthead = -7;
        tgttail = 18 + tgthead - 2;
        upoff = 7 - 1;
        downoff = -7 - 1;
    } else {
        if (Tgt->data == crab) {
            tgthead = -7;
            tgttail = 18 + tgthead - 2;
            upoff = 7 - 1;
            downoff = -7 - 1;
        } else {
            if (Tgt->data == missile) {
                tgthead = -15;
                tgttail = 59 + tgthead - 3;
                upoff = 9 - 1;
                downoff = -9 - 1;
            }
        }
    }
    if ((Spr00.hpos + dronetail > Tgt->hpos + tgthead) &&
        (Spr00.hpos + dronehead < Tgt->hpos + tgttail) &&
        (Spr00.vpos - dronedownoff > Tgt->vpos - upoff) &&
        (Spr00.vpos - droneupoff < Tgt->vpos - downoff)
        ) {
        return 1;
    }
    return 0;
}

So I just compare extents (rectangles) instead of being pixel perfect.

 

The rectangles are smaller than the real sprites. So you don't get a hit if just one pixel collides.

  • Like 1
Link to comment
Share on other sites

3 minutes ago, karri said:

The collision stuff in the ping-pong example should work.

 

But most people (including me) prefer to do the collision in C-code as it is more efficient than wasting a whole screen of bytes just for a collision buffer.

 

In the recent Titan game I check if my drone hits an object. For that purpose I wrote a small routine.


It is a bit more complex due to my desire to use shaped sprites and more fields in the sprite struct.


static unsigned char collide(sprite_t *Tgt) {
    signed char upoff = 10 - 1;
    signed char downoff = -10 + 1;
    signed char tgthead = -15;
    signed char tgttail = 15;
    signed char droneupoff = 11;
    signed char dronedownoff = 11 - 26;
    signed char dronehead = -26;
    signed char dronetail = 31 - 26;

    if (Tgt->data == ball || Tgt->data == ballhit) {
        tgthead = -7;
        tgttail = 18 + tgthead - 2;
        upoff = 7 - 1;
        downoff = -7 - 1;
    } else {
        if (Tgt->data == crab) {
            tgthead = -7;
            tgttail = 18 + tgthead - 2;
            upoff = 7 - 1;
            downoff = -7 - 1;
        } else {
            if (Tgt->data == missile) {
                tgthead = -15;
                tgttail = 59 + tgthead - 3;
                upoff = 9 - 1;
                downoff = -9 - 1;
            }
        }
    }
    if ((Spr00.hpos + dronetail > Tgt->hpos + tgthead) &&
        (Spr00.hpos + dronehead < Tgt->hpos + tgttail) &&
        (Spr00.vpos - dronedownoff > Tgt->vpos - upoff) &&
        (Spr00.vpos - droneupoff < Tgt->vpos - downoff)
        ) {
        return 1;
    }
    return 0;
}

 

Wow, so it's just old-school C programming. I should be able to manage that. If most folks are avoiding the built-in collision detection, I should avoid it, too. So I'll revert my *.cfg file, and I'll try to figure out what you're doing in your drone example. Or I'll just go with a more basic approach to start with. Either way, it seems that C is my friend.

Thank you for putting me back on the right path, all of you!

  • Like 2
Link to comment
Share on other sites

In my recent Titan game I have a giant list of sprites that are linked to each other. So a single call to draw the 1st sprite will draw the entire screen.

 

The sprites are linked to each other like this:


 

static sprite_t Sbg03 = {
    BPP_4 | TYPE_BACKGROUND,
    PACKED | REHV | REUSEPAL,
    NO_COLLIDE,
    (char *)&Sbg04,
    tile00,
    48, 0,
    0x100, 0x100,
    1
};

static sprite_t Sbg02 = {
    BPP_4 | TYPE_BACKGROUND,
    PACKED | REHV | REUSEPAL,
    NO_COLLIDE,
    (char *)&Sbg03,
    tile00,
    32, 0,
    0x100, 0x100,
    1
};

static sprite_t Sbg01 = {
    BPP_4 | TYPE_BACKGROUND,
    PACKED | REHV | REUSEPAL,
    NO_COLLIDE,
    (char *)&Sbg02,
    tile00,
    16, 0,
    0x100, 0x100,
    1
};

static sprite_t Sbg00 = {
    BPP_4 | TYPE_BACKGROUND,
    PACKED | REHV | REUSEPAL,
    NO_COLLIDE,
    (char *)&Sbg01,
    tile00,
    0, 0,
    0x100, 0x100,
    1
};

static SCB_REHV_PAL Stree = {
    BPP_4 | TYPE_BACKGROUND,
    PACKED | REHV,
    NO_COLLIDE,
    (char *)&Sbg00,
    tree,
    0, 0,
    160 * 0x100, 0x100,
    {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}
};

As you see, I define the palette only once. After that I mention REUSEPAL.

 

I also need an extra variable for each sprite to contain the health of that object. So I defined my own sprite with an extra field "h":

 

typedef struct {
    unsigned char sprctl0;
    unsigned char sprctl1;
    unsigned char sprcoll;
    char *next;
    unsigned char *data;
    signed int hpos;
    signed int vpos;
    unsigned int hsize;
    unsigned int vsize;
    unsigned char h;
} sprite_t;

By using the bit SKIP I can instruct Suzy to ignore some sprite.

 

Here is an example to fire a new bullet (I can have 50 bullets in the air simultaneously).

 

static void newshot() {
    sprite_t *Spr = (sprite_t*)&Spr00;
    while (Spr->next != (char *)&Spr51) {
        Spr = (sprite_t*)(Spr->next);
        if ((Spr->sprctl1 & SKIP) != 0) {
            Spr->sprctl1 = PACKED | REHV | REUSEPAL;
            Spr->hpos = Spr00.hpos;
            Spr->vpos = Spr00.vpos;
            Spr->data = shoote;
            return;
        }
    }
}

By using the SKIP to mark when a sprite is unused I don't need to use malloc. Instead I just traverse the list of sprites and toggle the sprite off (Set the SKIP bit on) when the item is no longer active.

titan3.png.ec6d9418ea7685e5667704c0db61391e.png

  • Like 1
Link to comment
Share on other sites

Thanks for explaining all that, Karri.

 

The sprite drawing syntax is a mystery to me. Not just yours, but in general. BPP_4 means 4 bits per pixel, REUSEPAL means to reuse the palette, and NO_COLLIDE means to turn off collision. But TYPE_BACKGROUND, PACKED, REHV, and the various numbers... I haven't been able to interpret all those. Is that C code, or is it CC65? Surely there's a document somewhere explaining the bits and pieces. I'm not asking you to explain it. You don't have to do that. I'm just curious if there's a resource somewhere that I haven't found yet.

 

static sprite_t Sbg00 = {
    BPP_4 | TYPE_BACKGROUND,
    PACKED | REHV | REUSEPAL,
    NO_COLLIDE,
    (char *)&Sbg01,
    tile00,
    0, 0,
    0x100, 0x100,
    1
};

 

Link to comment
Share on other sites

8 minutes ago, OldAtAtari said:

Thanks for explaining all that, Karri.

 

The sprite drawing syntax is a mystery to me. Not just yours, but in general. BPP_4 means 4 bits per pixel, REUSEPAL means to reuse the palette, and NO_COLLIDE means to turn off collision. But TYPE_BACKGROUND, PACKED, REHV, and the various numbers... I haven't been able to interpret all those. Is that C code, or is it CC65? Surely there's a document somewhere explaining the bits and pieces. I'm not asking you to explain it. You don't have to do that. I'm just curious if there's a resource somewhere that I haven't found yet.

 


static sprite_t Sbg00 = {
    BPP_4 | TYPE_BACKGROUND,
    PACKED | REHV | REUSEPAL,
    NO_COLLIDE,
    (char *)&Sbg01,
    tile00,
    0, 0,
    0x100, 0x100,
    1
};

RTFM ? (see link below)

  • Like 1
Link to comment
Share on other sites

30 minutes ago, 42bs said:

RTFM ? (see link below)

? ? There's a FM???? ? ? 

 

Thank you for the link and the kind advice. I'll read it!

 

Oh, now that I click on it, I see completely. It's the 1987 EPYX manual! I've been ignoring that one because I thought that all this CC65 stuff was new. I didn't realize they used it back then. That's so cool. Thanks again!

 

http://www.monlynx.de/lynx/lynxdoc.html

Edited by OldAtAtari
  • Haha 1
Link to comment
Share on other sites

8 hours ago, karri said:

In my recent Titan game I have a giant list of sprites that are linked to each other. So a single call to draw the 1st sprite will draw the entire screen.

 

The sprites are linked to each other like this:


 


static sprite_t Sbg03 = {
    BPP_4 | TYPE_BACKGROUND,
    PACKED | REHV | REUSEPAL,
    NO_COLLIDE,
    (char *)&Sbg04,
    tile00,
    48, 0,
    0x100, 0x100,
    1
};

static sprite_t Sbg02 = {
    BPP_4 | TYPE_BACKGROUND,
    PACKED | REHV | REUSEPAL,
    NO_COLLIDE,
    (char *)&Sbg03,
    tile00,
    32, 0,
    0x100, 0x100,
    1
};

static sprite_t Sbg01 = {
    BPP_4 | TYPE_BACKGROUND,
    PACKED | REHV | REUSEPAL,
    NO_COLLIDE,
    (char *)&Sbg02,
    tile00,
    16, 0,
    0x100, 0x100,
    1
};

static sprite_t Sbg00 = {
    BPP_4 | TYPE_BACKGROUND,
    PACKED | REHV | REUSEPAL,
    NO_COLLIDE,
    (char *)&Sbg01,
    tile00,
    0, 0,
    0x100, 0x100,
    1
};

static SCB_REHV_PAL Stree = {
    BPP_4 | TYPE_BACKGROUND,
    PACKED | REHV,
    NO_COLLIDE,
    (char *)&Sbg00,
    tree,
    0, 0,
    160 * 0x100, 0x100,
    {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}
};

As you see, I define the palette only once. After that I mention REUSEPAL.

 

I also need an extra variable for each sprite to contain the health of that object. So I defined my own sprite with an extra field "h":

 


typedef struct {
    unsigned char sprctl0;
    unsigned char sprctl1;
    unsigned char sprcoll;
    char *next;
    unsigned char *data;
    signed int hpos;
    signed int vpos;
    unsigned int hsize;
    unsigned int vsize;
    unsigned char h;
} sprite_t;

By using the bit SKIP I can instruct Suzy to ignore some sprite.

 

Here is an example to fire a new bullet (I can have 50 bullets in the air simultaneously).

 


static void newshot() {
    sprite_t *Spr = (sprite_t*)&Spr00;
    while (Spr->next != (char *)&Spr51) {
        Spr = (sprite_t*)(Spr->next);
        if ((Spr->sprctl1 & SKIP) != 0) {
            Spr->sprctl1 = PACKED | REHV | REUSEPAL;
            Spr->hpos = Spr00.hpos;
            Spr->vpos = Spr00.vpos;
            Spr->data = shoote;
            return;
        }
    }
}

By using the SKIP to mark when a sprite is unused I don't need to use malloc. Instead I just traverse the list of sprites and toggle the sprite off (Set the SKIP bit on) when the item is no longer active.

titan3.png.ec6d9418ea7685e5667704c0db61391e.png

Karri, I just played Titan. I downloaded it from AtariGamer.com. Awesome. I really like it, and I'm looking forward to playing it again. I love that it seems to side-scroll endlessly in both directions, and you can go back and forth, unlike other games where you can never revisit a screen you've passed. And the shots bouncing off at different angles makes it really interesting too, not to mention being able to pilot the drone behind some columns and in front of others. It's great inspiration for me, to see what can be done on the Lynx. I haven't tried the other new games that just went up on AtariGamer. Maybe tomorrow. You all do such great work.

  • Like 1
Link to comment
Share on other sites

I did a little reorganizing tonight. I was tired of Linux. I couldn't find a good emulator on there, and also my Linux-created pcx files wouldn't process through the assembler, regardless of whether I used Gimp or Grafx2. I had to create them in Gimp or Grafx2 in Windows and then port them over to Linux. And plus my little Linux machine is a bit weak, and the new Firefox freezes if I open too many tabs.

 

So a quick internet search showed me that you can install Ubuntu as an application in Windows. I actually tried cygwin first, but it was problematic. The Ubuntu install was simple, and recreating my development directory tree was easy, and game.c still compiles with no problem. So now I can have a Linux terminal window along with all the fancy Windows tools on the same screen. I'm happy.
 

For anyone who doesn't already know how to do this, here are the instructions I used to enable and install Linux on Windows 10: https://www.howtogeek.com/249966/how-to-install-and-use-the-linux-bash-shell-on-windows-10/

Link to comment
Share on other sites

The pcx encoding of files have changed recently in Gimp. All modern tools create different pcx format files. I have updated my tools to cope with the new and the old format. But most other tools will fail decoding pcx unless they are updated.

 

If you need to patch sp65 in some version have a look at my commit

https://bitbucket.org/atarilynx/lynx/commits/007a077f6b586c0c0a232b1f47a974f43ded05df

 

So if you are using my cc65 please do a

 

cd lynx/tools

git pull

make -f Makefile.deb

 

You also need to remove the old version before doing this.

  • Like 2
Link to comment
Share on other sites

1 hour ago, karri said:

The pcx encoding of files have changed recently in Gimp. All modern tools create different pcx format files. I have updated my tools to cope with the new and the old format. But most other tools will fail decoding pcx unless they are updated.

 

If you need to patch sp65 in some version have a look at my commit

https://bitbucket.org/atarilynx/lynx/commits/007a077f6b586c0c0a232b1f47a974f43ded05df

 

So if you are using my cc65 please do a

 

cd lynx/tools

git pull

make -f Makefile.deb

 

You also need to remove the old version before doing this.

Cool. I'll upgrade sp65 soon. Thanks.

Link to comment
Share on other sites

If you want to solve the pcx format issue, I would advise to not use pcx at all :)

 

What i do instead is to put all my sprites in a big sprite sheet and save it as a bmp in 24 bits. Im just making sure I dont have more than 16 colors used (all good tools can count the total number of colours used for you).

 

the only thing to be aware of is that if you use shades that are too close to each others, you might end up having the same colour twice in the palette.

Edited by LordKraken
  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...