static ERROR CMDInit(OBJECTPTR, struct KernelBase *);
static ERROR CMDExpunge(void);
#define VER_STARFIELD 1.0
EXPORT struct ModHeader ModHeader = {
MODULE_HEADER_V1, CMDInit, NULL, NULL, CMDExpunge,
JMP_DEFAULT, 0, CPU_PC, VER_STARFIELD, VER_KERNEL,
"Rocklyte Systems",
"Rocklyte Systems (c) 2000-2001. All rights reserved.",
"February 2001",
"Starfield"
};
static struct KernelBase *KernelBase = 0;
static OBJECTPTR StarfieldClass = 0;
/*****************************************************************************
** Starfield class.
*/
struct Starfield {
OBJECT_HEADER
OBJECTID DrawableID; /* */
LONG MaxElements; /* Maximum amount of stars to use */
LONG XOffset; /* Horizontal offset of the starfield center */
LONG YOffset; /* Vertical offset of the starfield center */
PRIVATE_FIELDS
OBJECTID TimerID;
struct Star *Stars;
MEMORYID StarsMID;
LONG DrawableWidth;
LONG DrawableHeight;
};
struct Star {
LONG XCoord;
LONG YCoord;
LONG XSpeed;
LONG YSpeed;
WORD TrailX;
WORD TrailY;
};
static struct FieldArray StarfieldFields[] = {
{ "Drawable", 0, FDF_OBJECTID|FDF_RI, ID_RENDER, NULL, NULL },
{ "MaxElements", 0, FDF_LONG|FDF_RI, 0, NULL, NULL },
{ "XOffset", 0, FDF_LONG|FDF_RW, 0, NULL, NULL },
{ "YOffset", 0, FDF_LONG|FDF_RW, 0, NULL, NULL },
END_FIELD
};
static ERROR STARFIELD_ActionNotify(struct Starfield *, struct acActionNotify *);
static ERROR STARFIELD_Free(struct Starfield *, APTR);
static ERROR STARFIELD_ReleaseObject(struct Starfield *, APTR);
static ERROR STARFIELD_NewObject(struct Starfield *, APTR);
static ERROR STARFIELD_Init(struct Starfield *, APTR);
static ERROR STARFIELD_Move(struct Starfield *, struct acMove *);
static ERROR STARFIELD_Scroll(struct Starfield *, struct acScroll *);
static ERROR STARFIELD_Timer(struct Starfield *, struct acTimer *);
static struct ActionArray StarfieldActions[] = {
{ AC_ActionNotify, STARFIELD_ActionNotify },
{ AC_Free, STARFIELD_Free },
{ AC_ReleaseObject, STARFIELD_ReleaseObject },
{ AC_Init, STARFIELD_Init },
{ AC_Move, STARFIELD_Move },
{ AC_NewObject, STARFIELD_NewObject },
{ AC_Scroll, STARFIELD_Scroll },
{ AC_Timer, STARFIELD_Timer },
{ NULL, NULL }
};
/*****************************************************************************
** Command: Init()
*/
ERROR CMDInit(OBJECTPTR argModule, struct KernelBase *argKernelBase)
{
KernelBase = argKernelBase;
return(CreateObject(ID_CLASS, NULL, (OBJECTPTR *)&StarfieldClass, NULL,
FID_BaseClassID|TLONG, ID_STARFIELD,
FID_Version|TFLOAT, VER_STARFIELD,
FID_Name|TSTRING, "Starfield",
FID_Category|TLONG, CCF_EFFECT,
FID_Actions|TPTR, StarfieldActions,
FID_Fields|TPTR, StarfieldFields,
FID_Size|TLONG, sizeof(struct Starfield),
TAGEND));
}
/*****************************************************************************
** Command: Expunge()
*/
ERROR CMDExpunge(void)
{
if (StarfieldClass) { Action(AC_Free, StarfieldClass, NULL); StarfieldClass = NULL; }
return(ERR_Okay);
}
/*****************************************************************************
** Function: GenerateStar()
*/
static void GenerateStar(struct Star *Star)
{
#define MAXSPEED 18
#define MINSPEED 2
Star->XCoord = 0;
Star->YCoord = 0;
Star->XSpeed = 0;
Star->YSpeed = 0;
while ((Star->XSpeed > -MINSPEED) AND (Star->XSpeed < +MINSPEED) AND
(Star->YSpeed > -MINSPEED) AND (Star->YSpeed < +MINSPEED)) {
Star->XSpeed = (RandomNumber(MAXSPEED) - (MAXSPEED>>1) + (((FLOAT)RandomNumber(10000))/10000)) * 0xffff;
Star->YSpeed = (RandomNumber(MAXSPEED) - (MAXSPEED>>1) + (((FLOAT)RandomNumber(10000))/10000)) * 0xffff;
}
}
/*****************************************************************************
** Starfield: ActionNotify
*/
static ERROR STARFIELD_ActionNotify(struct Starfield *Self, struct acActionNotify *NotifyArgs)
{
if ((NotifyArgs->ActionID IS AC_Draw) IS (NotifyArgs->Error IS ERR_Okay)) {
struct RGB rgb;
struct Bitmap *bitmap;
OBJECTPTR Drawable;
WORD offsetx, offsety, XQuadrant, YQuadrant, x, y;
LONG ColourX, ColourY, i;
if (!Self->Stars) {
if (AccessMemory(Self->StarsMID, MEM_READWRITE, (void **)&Self->Stars) != ERR_Okay) {
return(ObjectError(Self, ERH_Draw, ERR_AccessMemory));
}
}
if (AccessObject(Self->DrawableID, 0, &Drawable) != ERR_Okay) {
return(ObjectError(Self, ERH_Draw, ERR_ExclusiveDenied));
}
if ((GetField(Drawable, FID_Bitmap, FT_POINTER, &bitmap) != ERR_Okay) OR (!bitmap)) {
ReleaseObject(Drawable);
return(ObjectError(Self, ERH_Draw, ERR_GetField));
}
GetFields(Drawable, FID_Width|TLONG, &Self->DrawableWidth,
FID_Height|TLONG, &Self->DrawableHeight,
TAGEND);
XQuadrant = Self->DrawableWidth>>1;
YQuadrant = Self->DrawableHeight>>1;
offsetx = Self->XOffset + (Self->DrawableWidth>>1);
offsety = Self->YOffset + (Self->DrawableHeight>>1);
if ((XQuadrant > 0) AND (YQuadrant > 0)) { /* Must be > 0 to prevent division by zero */
/*** Update the starfield ***/
for (i=0; i < Self->MaxElements; i++) {
ColourX = Self->Stars[i].XCoord>>16;
if (ColourX < 0) ColourX = -ColourX;
ColourX = (ColourX * (256-32) / XQuadrant) + 32;
ColourY = Self->Stars[i].YCoord>>16;
if (ColourY < 0) ColourY = -ColourY;
ColourY = (ColourY * (256-32) / YQuadrant) + 32;
if (ColourY > ColourX) ColourX = ColourY;
rgb.Red = ColourX;
rgb.Green = ColourX;
rgb.Blue = ColourX;
x = offsetx + (WORD)(Self->Stars[i].XCoord>>16);
y = offsety + (WORD)(Self->Stars[i].YCoord>>16);
if ((x >= bitmap->ClipLeft) AND (x < bitmap->ClipRight) AND
(y >= bitmap->ClipTop) AND (y < bitmap->ClipBottom)) {
bitmap->DrawUCRPixel(bitmap, bitmap->XOffset + x, bitmap->YOffset + y, &rgb);
}
}
}
ReleaseObject(Drawable);
}
return(ERR_Okay);
}
/*****************************************************************************
** Starfield: Free
*/
static ERROR STARFIELD_Free(struct Starfield *Self, APTR Void)
{
UnsubscribeTimer(Self->Head.UniqueID);
if (Self->DrawableID) {
OBJECTPTR Drawable;
if (AccessObject(Self->DrawableID, 2000, &Drawable) IS ERR_Okay) {
UnsubscribeAction(Drawable, NULL, GetUniqueID(Self));
ReleaseObject(Drawable);
}
}
if (Self->Stars) { ReleaseMemory(Self->Stars); Self->Stars = NULL; }
if (Self->StarsMID) { FreeMemoryID(Self->StarsMID); Self->StarsMID = NULL; }
return(ERR_Okay);
}
/*****************************************************************************
** Starfield: ReleaseObject
*/
static ERROR STARFIELD_ReleaseObject(struct Starfield *Self, APTR Void)
{
if (Self->Stars) { ReleaseMemory(Self->Stars); Self->Stars = NULL; }
return(ERR_Okay);
}
/*****************************************************************************
** Starfield: Init
*/
static ERROR STARFIELD_Init(struct Starfield *Self, APTR Void)
{
OBJECTPTR Drawable;
LONG i;
if (Self->MaxElements > 10000) Self->MaxElements = 10000;
/*** Find the Drawable object that we are associated with ***/
if (!Self->DrawableID) {
OBJECTID OwnerID;
OwnerID = GetOwner(Self);
while ((OwnerID) AND (GetClassID(OwnerID) != ID_RENDER)) {
OwnerID = GetOwnerID(OwnerID);
}
if (!OwnerID) return(ObjectError(Self, ERH_Init, ERR_UnsupportedOwner));
else Self->DrawableID = OwnerID;
}
/*** Calculate the starfield ***/
if (AllocMemory(sizeof(struct Star) * Self->MaxElements, Self->Head.MemFlags|MEM_NOCLEAR,
(APTR *)&Self->Stars, &Self->StarsMID) IS ERR_Okay) {
for (i=0; i < Self->MaxElements; i++) {
GenerateStar(&Self->Stars[i]);
}
}
else return(ObjectError(Self, ERH_Init, ERR_Memory));
/*** Action subscriptions ***/
if (AccessObject(Self->DrawableID, 200, &Drawable) IS ERR_Okay) {
SubscribeAction(Drawable, AC_Draw, Self);
ReleaseObject(Drawable);
}
/*** Initialise the Timer ***/
return(SubscribeTimer(Self->Head.UniqueID, 20));
}
/*****************************************************************************
** Action: Move
*/
static ERROR STARFIELD_Move(struct Starfield *Self, struct acMove *Args)
{
Self->XOffset += Args->XChange;
Self->YOffset += Args->YChange;
return(ERR_Okay);
}
/*****************************************************************************
** Starfield: NewObject
*/
static ERROR STARFIELD_NewObject(struct Starfield *Self, APTR Args)
{
Self->MaxElements = 500;
return(ERR_Okay);
}
/*****************************************************************************
** Action: Scroll
*/
static ERROR STARFIELD_Scroll(struct Starfield *Self, struct acScroll *Args)
{
Self->XOffset += Args->XChange;
Self->YOffset += Args->YChange;
return(ERR_Okay);
}
/*****************************************************************************
** Action: Timer
*/
static ERROR STARFIELD_Timer(struct Starfield *Self, struct acTimer *Args)
{
LONG XCoord, YCoord, i, offsetx, offsety;
if (!Self->Stars) {
if (AccessMemory(Self->StarsMID, MEM_READWRITE, (void **)&Self->Stars) != ERR_Okay) {
return(ObjectError(Self, ERH_Draw, ERR_AccessMemory));
}
}
offsetx = Self->XOffset + (Self->DrawableWidth>>1);
offsety = Self->YOffset + (Self->DrawableHeight>>1);
for (i=0; i < Self->MaxElements; i++) {
XCoord = offsetx + (WORD)(Self->Stars[i].XCoord>>16);
YCoord = offsety + (WORD)(Self->Stars[i].YCoord>>16);
if ((XCoord >= 0) AND (XCoord < Self->DrawableWidth) AND
(YCoord >= 0) AND (YCoord < Self->DrawableHeight)) {
Self->Stars[i].TrailX = XCoord;
Self->Stars[i].TrailY = YCoord;
Self->Stars[i].XCoord += Self->Stars[i].XSpeed;
Self->Stars[i].YCoord += Self->Stars[i].YSpeed;
}
else GenerateStar(&Self->Stars[i]);
}
DelayMsg(AC_Draw, Self->DrawableID, NULL);
return(ERR_Okay);
}
/*****************************************************************************
###FIELD###
Name: MaxElements
Short: The total number of stars is defined by this field.
Type: LONG
Status: Read/Init
###DESCRIPTION###
You may specify the exact amount of stars to be used in the Starfield by
setting the MaxElements field. For a reasonable effect, a minimum amount
of 500 stars is recommended. The value should be no higher than 5000.
If you do not set this field, the default number of stars will be used.
###END###
*****************************************************************************/
/*****************************************************************************
###FIELD###
Name: XOffset
Short: The horizontal location of the Starfield's center.
Type: LONG
Status: Read/Write
###DESCRIPTION###
By default this field is set to zero, which puts the horizontal
location of the Starfield at the center of its container. You can
manipulate the horizontal center of the Starfield by writing directly to
this field. Negative values will move the Starfield to the left,
positive values will move it to the right.
###END###
*****************************************************************************/
/*****************************************************************************
###FIELD###
Name: YOffset
Short: The vertical location of the Starfield's center.
Type: LONG
Status: Read/Write
###DESCRIPTION###
By default this field is set to zero, which puts the vertical location
of the Starfield at the center of its container. You can manipulate the
vertical center of the Starfield by writing directly to this field.
Negative values will move the Starfield up, positive values will move
it down.
###END###
*****************************************************************************/