A_FlickyCenter is an action used to spawn a new Flicky from the actor's position, although it can be used to spawn any Object. Differently from A_FlickySpawn, its intended use is spawning enemies from Things directly placed on a map. If Var1's lower 16 bits are set to 0, a random flicky type chosen from the FlickyList level header parameter is spawned; otherwise, the designated Object type is spawned. The 17th-20th bits determine the spawned flicky's color from a preset list if the flicky is a Fish. Additionally, the actor's Object flags toggle extra miscellaneous behavior. It's important to note that, if this action is called by an actor placed via a Thing, then that Thing's properties will take precedence over the actor's: the Thing's flags will override the actor's Object flags, and its Parameter value will pick the flicky's color instead.
Flicky behavior settings
Object flags |
Thing flags |
Behavior
|
MF_SLIDEME |
Extra flag |
Instead of orbiting its target, the spawned flicky will move aimlessly.
|
MF_GRENADEBOUNCE |
Special flag |
Flickies will not move and will be suspended in mid-air. If the actor also has MF_NOCLIPTHING , they will be allowed to hop in place.
|
MF_NOCLIPTHING |
Ambush flag |
Used in tandem with MF_GRENADEBOUNCE to give still flickies gravity.
|
Available colors for Fish
Value |
Color
|
0
|
Random pick between SKINCOLOR_RED , SKINCOLOR_CYAN or SKINCOLOR_BLUE
|
1
|
SKINCOLOR_RED
|
2
|
SKINCOLOR_CYAN
|
3
|
SKINCOLOR_BLUE
|
4
|
SKINCOLOR_VAPOR
|
5
|
SKINCOLOR_PURPLE
|
6
|
SKINCOLOR_BUBBLEGUM
|
7
|
SKINCOLOR_NEON
|
8
|
SKINCOLOR_BLACK
|
9
|
SKINCOLOR_BEIGE
|
10
|
SKINCOLOR_LAVENDER
|
11
|
SKINCOLOR_RUBY
|
12
|
SKINCOLOR_SALMON
|
13
|
SKINCOLOR_SUNSET
|
14
|
SKINCOLOR_ORANGE
|
15
|
SKINCOLOR_YELLOW
|
Var2 sets the maximum default distance that the spawned flicky can move away from its target, in fracunits. If the actor was spawned from a Thing, then that Thing's Angle value takes precedence.
This action will additionally set the spawned flicky as the actor's tracer, and if called continuously, handle its targeting behavior. It will keep looking for a player around its active radius, and if there is one, it'll constantly move to where that player is; if that player backs off from that radius, it will then start moving back to its spawn point.
Code – A_FlickyCenter
|
|
// Function: A_FlickyCenter
//
// Description: Place flickies in-level.
//
// var1:
// Lower 16 bits = if 0, spawns random flicky based on level header. Else, spawns the designated thing type.
// Bits 17-20 = Flicky color, up to 15. Applies to fish.
// Bit 21 = Flag MF_SLIDEME (see below)
// Bit 22 = Flag MF_GRENADEBOUNCE (see below)
// Bit 23 = Flag MF_NOCLIPTHING (see below)
//
// If actor is placed from a spawnpoint (map Thing), the Thing's properties take precedence.
//
// var2 = maximum default distance away from spawn the flickies are allowed to travel. If angle != 0, then that's the radius.
//
// If MTF_EXTRA (MF_SLIDEME): is flagged, Flickies move aimlessly. Else, orbit around the target.
// If MTF_OBJECTSPECIAL (MF_GRENADEBOUNCE): Flickies stand in-place without gravity (unless they hop, then gravity is applied.)
// If MTF_AMBUSH (MF_NOCLIPTHING): is flagged, Flickies hop.
//
void A_FlickyCenter(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
UINT16 flickytype = (locvar1 & 0xFFFF);
UINT8 flickycolor = ((locvar1 >> 16) & 0xFF);
UINT8 flickyflags = ((locvar1 >> 20) & 0xF);
if (LUA_CallAction(A_FLICKYCENTER, actor))
return;
if (!actor->tracer)
{
mobj_t *flicky = P_InternalFlickySpawn(actor, locvar1, 1, false, 0);
P_SetTarget(&flicky->target, actor);
P_SetTarget(&actor->tracer, flicky);
if (actor->spawnpoint)
{
actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING);
actor->flags |= (
((actor->spawnpoint->options & MTF_EXTRA) ? MF_SLIDEME : 0)
| ((actor->spawnpoint->options & MTF_OBJECTSPECIAL) ? MF_GRENADEBOUNCE : 0)
| ((actor->spawnpoint->options & MTF_AMBUSH) ? MF_NOCLIPTHING : 0)
);
actor->extravalue1 = actor->spawnpoint->angle ? abs(actor->spawnpoint->angle) * FRACUNIT
: locvar2 ? abs(locvar2) : 384 * FRACUNIT;
actor->extravalue2 = actor->spawnpoint->extrainfo;
actor->friction = actor->spawnpoint->x*FRACUNIT;
actor->movefactor = actor->spawnpoint->y*FRACUNIT;
actor->watertop = actor->spawnpoint->z*FRACUNIT;
}
else
{
actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING);
actor->flags |= (
((flickyflags & 1) ? MF_SLIDEME : 0)
| ((flickyflags & 2) ? MF_GRENADEBOUNCE : 0)
| ((flickyflags & 4) ? MF_NOCLIPTHING : 0)
);
actor->extravalue1 = abs(locvar2);
actor->extravalue2 = flickycolor;
actor->friction = actor->x;
actor->movefactor = actor->y;
actor->watertop = actor->z;
locvar1 = flickytype;
}
if (actor->flags & MF_GRENADEBOUNCE) // in-place
actor->tracer->fuse = 0;
else if (actor->flags & MF_SLIDEME) // aimless
{
actor->tracer->fuse = 0; // less than 2*TICRATE means move aimlessly.
actor->tracer->angle = P_RandomKey(180)*ANG2;
}
else //orbit
actor->tracer->fuse = FRACUNIT;
if (locvar1 == MT_FLICKY_08)
P_InternalFlickySetColor(actor->tracer, actor->extravalue2);
actor->extravalue2 = 0;
}
if (!(actor->flags & MF_SLIDEME) && !(actor->flags & MF_GRENADEBOUNCE))
{
fixed_t originx = actor->friction;
fixed_t originy = actor->movefactor;
fixed_t originz = actor->watertop;
actor->tracer->fuse = FRACUNIT;
// Impose default home radius if flicky orbits around player
if (!actor->extravalue1)
actor->extravalue1 = locvar2 ? abs(locvar2) : 384 * FRACUNIT;
P_LookForPlayers(actor, true, false, actor->extravalue1);
if (actor->target && P_AproxDistance(actor->target->x - originx, actor->target->y - originy) < actor->extravalue1)
{
actor->extravalue2 = 1;
P_TeleportMove(actor, actor->target->x, actor->target->y, actor->target->z);
}
else if(actor->extravalue2)
{
actor->extravalue2 = 0;
P_TeleportMove(actor, originx, originy, originz);
}
}
}
|
|
Code – P_InternalFlickySetColor
|
|
// Internal Flicky color setting
void P_InternalFlickySetColor(mobj_t *actor, UINT8 extrainfo)
{
UINT8 flickycolors[] = {
SKINCOLOR_RED,
SKINCOLOR_CYAN,
SKINCOLOR_BLUE,
SKINCOLOR_VAPOR,
SKINCOLOR_PURPLE,
SKINCOLOR_BUBBLEGUM,
SKINCOLOR_NEON,
SKINCOLOR_BLACK,
SKINCOLOR_BEIGE,
SKINCOLOR_LAVENDER,
SKINCOLOR_RUBY,
SKINCOLOR_SALMON,
SKINCOLOR_SUNSET,
SKINCOLOR_ORANGE,
SKINCOLOR_YELLOW,
};
if (extrainfo == 0)
// until we can customize flicky colors by level header, just stick to SRB2's defaults
actor->color = flickycolors[P_RandomKey(2)]; //flickycolors[P_RandomKey(sizeof(flickycolors))];
else
actor->color = flickycolors[min(extrainfo-1, 14)]; // sizeof(flickycolors)-1
}
|
|
Code – P_InternalFlickySpawn
|
|
// Internal Flicky spawning function.
mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers, SINT8 moveforward)
{
mobj_t *flicky;
fixed_t offsx = 0, offsy = 0;
if (!flickytype)
{
if (!mapheaderinfo[gamemap-1] || !mapheaderinfo[gamemap-1]->numFlickies) // No mapheader, no shoes, no service.
return NULL;
else
{
INT32 prandom = P_RandomKey(mapheaderinfo[gamemap-1]->numFlickies);
flickytype = mapheaderinfo[gamemap-1]->flickies[prandom];
}
}
if (moveforward)
{
fixed_t scal = mobjinfo[flickytype].radius*((fixed_t)moveforward);
offsx = P_ReturnThrustX(actor, actor->angle, scal);
offsy = P_ReturnThrustY(actor, actor->angle, scal);
}
flicky = P_SpawnMobjFromMobj(actor, offsx, offsy, 0, flickytype);
flicky->angle = actor->angle;
if (flickytype == MT_SEED)
flicky->z += P_MobjFlip(actor)*(actor->height - flicky->height)/2;
if (actor->eflags & MFE_UNDERWATER)
momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
P_SetObjectMomZ(flicky, momz, false);
flicky->movedir = (P_RandomChance(FRACUNIT/2) ? -1 : 1);
flicky->fuse = P_RandomRange(595, 700); // originally 300, 350
flicky->threshold = 0;
if (lookforplayers)
P_LookForPlayers(flicky, true, false, 0);
return flicky;
}
|
|