/* STAR TERROR! */
// Given that Faw Labo has teased us over the last several years with many
// unreleased demos/games, all basically the same same code of a graphical
// intro and a star field,
// https://www.youtube.com/watch?v=qi0LYXnZ_IU
// https://www.youtube.com/watch?v=ZkawFHJf9Fk
// https://www.youtube.com/watch?v=pABpAVcFeiI
// https://www.youtube.com/watch?v=Felzgc1ynJc
// https://www.youtube.com/watch?v=cJIzZnYxnl4
// https://www.youtube.com/watch?v=L0CIdx2Ri3w
// https://www.youtube.com/watch?v=qK7plhQTxDc
// I thought I might encourage some of our coders to take on those projects,
// and finish/release something playable, ... so here is a skeleton for a
// similar game, with a firing sight and star movement (attacking or retreating, your choice).
// There are plenty of free cycles left for the gameplay.
// (I ripped this out of my tailgunner code as you probably guessed)
// Just add some bad guys to shoot at and maybe a big banner intro...
// Note that a 'star trek' banner could be extracted from:
// https://commons.wikimedia.org/wiki/File:Star_Trek_TOS_Logo.svg#/media/File:Star_Trek_TOS_Logo.svg
// convert the SVG using https://atariage.com/forums/topic/237191-sinz-v07-vectrex-demo-playing-around-with-sinus/ ...
// and you can probably find the star trek or star wars themes online and massage them to work with
// the vectrex sound chip, eg http://www.lotro-abc.com/abc/startrek.abc
// The code below took me about 2 hours to put together; I'ld go on to make an actual game
// out of it if I had more time, but I swore to my colleagues in the PiTrex project that
// I would spend this weekend working on the MAME port, which is more urgent than this hack...
// Graham. 31 Aug 2019.
#include <vectrex.h>
static long rseed = 12345L;
static inline long urandom16 (void) {
return (rseed = (rseed * 2053) + 13849);
}
static inline unsigned long irand (long max) { // returns int 0..max-1
return (unsigned long) (urandom16 () % max); // probably not very random, but this is a video game, not crypto
}
static inline long pickrandom (long low, long high) { // We'll just assume that high > low...
return (long) irand (high - low + 1) + low; // pick a random integer between low and high inclusive.
}
static int stardots[8]; // fill with randoms ONCE - could move this into ROM...
static int rotated3[8]; // rotate stardots into place
static int rotated2[8];
static int rotated1[8];
static int rotated0[8];
static int *rotated[4] = {
rotated0,
rotated1,
rotated2,
rotated3
};
static unsigned int starfield_scale;
#define jsrx(v, func) asm("ldx %0\n\t" \
"jsr " #func "\n\t" : : "g" (v) : "d", "x")
#define Draw_VLp(ra) jsrx(ra, 0xF410) // (pattern y x)* 0x01
static inline void set_scale (unsigned int scale) {
*(volatile unsigned char *) 0xD004 = scale; /*VIA_t1_cnt_lo */
}
static inline void line (int y, int x) {
*(volatile unsigned char *) 0xC823 = 0;
Draw_Line_d ((int) y, (int) x);
}
static inline void dots (unsigned int size, const int *array) {
*(volatile char *) 0xC823 = size - 1;
Dot_List ((void *) array);
}
static inline void rotate_vl (unsigned int angle, unsigned int points, const void *original, void *rotated) {
*(volatile unsigned int *) 0xC836 = angle;
*(volatile unsigned int *) 0xC823 = points - 1;
Rot_VL ((int *) original, rotated);
}
#define joybit Joy_Digital
#define tstat (*((volatile unsigned int *) 0xc856))
#define pot0 (*((volatile int *) 0xc81b))
#define pot1 (*((volatile int *) 0xc81c))
#define pot2 (*((volatile int *) 0xc81d))
#define pot3 (*((volatile int *) 0xc81e))
#define epot0 (*((volatile unsigned int *) 0xc81f))
#define epot1 (*((volatile unsigned int *) 0xc820))
#define epot2 (*((volatile unsigned int *) 0xc821))
#define epot3 (*((volatile unsigned int *) 0xc822))
#define t1lolc (*((volatile unsigned int *) 0xd004))
#define read_ram(x) (*((volatile int *) x))
#define joystick1_button1 (int)(read_ram(0xC80F) & 1U) /* A */
#define joystick1_button2 (int)(read_ram(0xC80F) & 2U) /* S */
#define joystick1_button3 (int)(read_ram(0xC80F) & 4U) /* D */
#define joystick1_button4 (int)(read_ram(0xC80F) & 8U) /* F */
static int XJoy, YJoy, button1, button2, button3, button4;
static void poll_joystick (void) {
joybit ();
if (pot0 < -10) { XJoy = -1; // Left
} else if (pot0 > 10) { XJoy = 1; // Right
} else { XJoy = 0;
}
if (pot1 < -10) { YJoy = -1; // Down
} else if (pot1 > 10) { YJoy = 1; // Up
} else { YJoy = 0;
}
asm ("pshs x,y,u,dp");
asm ("lda #0");
asm ("jsr 0HF1B4 ; read_btns_mask":::"a", "b", "d");
asm ("puls dp,u,y,x");
button1 = joystick1_button1;
button2 = joystick1_button2;
button3 = joystick1_button3;
button4 = joystick1_button4;
}
static void init_hardware (void) {
// setup joystick read function to read only joystick 1
epot0 = 1;
epot1 = 3;
epot2 = 0;
epot3 = 0;
}
static inline void init_stars (void) {
starfield_scale = (unsigned int) 0xFF;
// origin at 0,0
stardots[0] = -60;
stardots[1] = (int) pickrandom (-45, 45); // LEFT
stardots[2] = 60;
stardots[3] = (int) pickrandom (-45, 45); // RIGHT
stardots[4] = (int) pickrandom (-45, 45);
stardots[5] = 60; // TOP
stardots[6] = (int) pickrandom (-45, 45);
stardots[7] = -60; // BOT
stardots[6] -= stardots[4];
stardots[7] -= stardots[5]; // abs to rel
stardots[4] -= stardots[2];
stardots[5] -= stardots[3];
stardots[2] -= stardots[0];
stardots[3] -= stardots[1];
rotate_vl ((unsigned int) (pickrandom (0, 15)), 4U, stardots, rotated0);
rotate_vl ((unsigned int) (pickrandom (16, 31)), 4U, stardots, rotated1);
rotate_vl ((unsigned int) (pickrandom (32, 47)), 4U, stardots, rotated2);
rotate_vl ((unsigned int) (pickrandom (48, 63)), 4U, stardots, rotated3);
}
static void stars (int speed) { // speed must be a power of 2
unsigned int shifted_scale;
int i;
shifted_scale = starfield_scale - (unsigned int) 0xBF;
for (i = 0; i < 4; i++) {
if ((unsigned int) shifted_scale > (unsigned int) 0x20) { // disappear here
Reset0Ref ();
Intensity_a ((shifted_scale >> 1) - 0x10); // fade with distance
set_scale (shifted_scale);
dots (4, rotated[i]);
} else {
if (((unsigned int) shifted_scale & (unsigned int) 0x3F) == 0U) {
// pull in new ring of stars
rotate_vl ((unsigned int) (pickrandom (0, 63)), 4U, stardots,
rotated[i]);
}
}
shifted_scale += 0x40;
}
starfield_scale -= (unsigned int) speed; // must be power of 2
}
static const int crosshair[] = {
0, 17, 40, // trying to move it to the center
-1, -30, 0,
0, 15, 20,
-1, 0, -115,
0, -15, 20,
-1, 30, 0,
0, -10, 20,
-1, -10, 0,
0, -10, 10,
-1, 0, 10,
0, -15, 10,
-1, 0, -30,
0, -20, 15,
-1, 105, 0,
0, -20, 15,
-1, 0, -30,
0, -20, 10,
-1, 0, 10,
0, -10, 10,
-1, -10, 0,
1
};
static int crosshair_x, crosshair_y;
static void follow_crosshair (void) {
XJoy += XJoy; XJoy += XJoy; YJoy += YJoy; YJoy += YJoy;
// constrain joysticks but carefully avoid integer overflow
if ((crosshair_x >= 0) && (XJoy > 0)
&& (crosshair_x >= 127 - XJoy)) /* ignore */ ;
else if ((crosshair_x < 0) && (XJoy < 0)
&& (crosshair_x < -127 - XJoy)) /* ignore */ ;
else
crosshair_x += XJoy;
if ((crosshair_y >= 0) && (YJoy > 0)
&& (crosshair_y >= 127 - YJoy)) /* ignore */ ;
else if ((crosshair_y < 0) && (YJoy < 0)
&& (crosshair_y < -127 - YJoy)) /* ignore */ ;
else
crosshair_y += YJoy;
Reset0Ref ();
set_scale (0x7f);
Moveto_d (crosshair_y, crosshair_x);
set_scale (0x18);
Draw_VLp (crosshair);
}
int main (void) {
long i;
int starspeed = -4;
init_hardware ();
init_stars ();
while (1) {
for (i = 0L; i < 400L; i++) {
Wait_Recal ();
Reset0Ref ();
Intensity_a (0x7F);
Print_Str_d (0, -50,
(starspeed < 0 ? "FORWARD\x80" : "REVERSE\x80"));
poll_joystick ();
follow_crosshair ();
if (button4) { // firing lasers
// You could patterns these lines and flicker them off and on for a better effect, like tracer shot...
Reset0Ref ();
set_scale (0x7fU);
Moveto_d (-128, -128);
set_scale (0xff);
line ((int) (((long) crosshair_y + 128L) / 2L),
(int) (((long) crosshair_x + 128L) / 2L));
// draw line to bottom left corner
Reset0Ref ();
set_scale (0x7fU);
Moveto_d (-128, 127);
set_scale (0xff);
line ((int) (((long) crosshair_y + 128L) / 2L),
(int) (((long) crosshair_x - 128L) / 2L));
// draw line to bottom right corner
}
stars (starspeed);
}
starspeed = -starspeed; // just for demo.
}
return 0;
}