Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Death Circle | SmokeNC | Mutator | 9 |
/* Death Circle mutator for LRS, written by Smoke[NC] and VioletCLM DM april 2023 */
/* VERSION 1.0 */
/* CONFIG VARIABLES */
/* Speed of moving the center of the circle */
const float cSpeed = 1;
/* Minimum radius that the circle reaches after defaultCompletionTime */
const float rMin = 300;
/* Time it takes to reach minimum radius, in seconds */
const float defaultCompletionTime = 120;
const uint8 ChromaKeyIndex = 25;
int LayerZ;
void onLevelLoad()
{
auto layers = jjLayerOrderGet();
LayerZ = 4 - layers.findByRef(jjLayers[4]) + 1;
jjLAYER storm(1,1);
storm.spriteMode = SPRITE::CHROMAKEY;
storm.spriteParam = ChromaKeyIndex;
storm.textureSurface = SURFACE::FULLSCREEN;
storm.xSpeed = storm.ySpeed = 1.1;
jjPIXELMAP stormImage(TEXTURE::DESOLATION);
array<uint8> recolor(256);
for (uint i = 0; i < 32; ++i)
recolor[176 + i] = ChromaKeyIndex + uint8(i / 5.2);
stormImage.recolor(recolor).makeTexture(storm);
storm.textureStyle = TEXTURE::WAVE;
storm.tileWidth = storm.tileHeight = true;
layers.insertAt(0, storm);
jjLayerOrderSet(layers);
//mess with these numbers and see what looks good
storm.wave.amplitudeX = 0.125;
storm.wave.wavelengthX = 127;
storm.wave.amplitudeY = 0.25;
storm.wave.wavelengthY = 255;
storm.wave.waveSpeed = 3;
}
float max(float x, float y)
{
return x > y? x : y;
}
class Point
{
float x;
float y;
}
class DeathCircle
{
float r; /* Radius */
Point c; /* Center */
float rSpeed;
float rOrig;
/* Death circle will always move to center of mass of the players */
Point CalcCenterOfMass()
{
Point cNew;
cNew.x = 0;
cNew.y = 0;
int numPlayersInGame = 0;
for(int iP = 0; iP < 32; iP++)
{
if(jjPlayers[iP].isInGame && !jjPlayers[iP].isZombie)
{
numPlayersInGame++;
cNew.x += jjPlayers[iP].xPos;
cNew.y += jjPlayers[iP].yPos;
}
}
if(numPlayersInGame != 0)
{
cNew.x /= numPlayersInGame;
cNew.y /= numPlayersInGame;
}
return cNew;
}
void UpdateSpeed(float completionTime)
{
if(completionTime > 0)
{
rSpeed = (r - rMin) / (completionTime * 70);
}
else /* Special effect, expand the circle!*/
{
rSpeed = (rOrig - rMin) / (completionTime * 70);
}
}
DeathCircle()
{
r = 32 * sqrt(jjLayerWidth[4] * jjLayerWidth[4] + jjLayerHeight[4] * jjLayerHeight[4]) / 2 ;
rOrig = r;
UpdateSpeed(defaultCompletionTime);
c.x = 32 * jjLayerWidth[4] / 2;
c.y = 32 * jjLayerHeight[4] / 2;
}
bool GoTo(float objx1, float objy1, float & out objx, float & out objy, float Startx, float Starty, float Endx, float Endy, float Speed, float Behind)
{
float angle = atan2((Endx - Startx), (Endy - Starty));
objx1 += sin(angle) * Speed;
objy1 += cos(angle) * Speed;
objx = objx1;
objy = objy1;
if (((Endx + Behind * sin(angle) - objx1) * (Endx + Behind * sin(angle) - objx1)
+ (Endy + Behind * cos(angle) - objy1) * (Endy + Behind * cos(angle) - objy1))
< (Speed * 1.1 + 3) * (Speed * 1.1 + 3))
return true; //arrived to destination
else return false; //not arrived to destination
}
void UpdateCircle()
{
Point cNew = CalcCenterOfMass();
Point cMaybeNew;
bool arrived = GoTo(c.x, c.y, cMaybeNew.x, cMaybeNew.y, c.x, c.y, cNew.x, cNew.y, cSpeed, 0);
/* To avoid circle jitter */
if(!arrived)
{
c = cMaybeNew;
}
r = max(rMin, r - rSpeed);
}
bool OutsideCircle(float x, float y)
{
return (x - c.x) * (x - c.x) + (y - c.y) * (y - c.y) > r * r;
}
}
DeathCircle deathCircle;
void onPlayer(jjPLAYER@ player)
{
/* DRAW DEATH CIRCLE*/
int height = jjResolutionHeight + 5;
int width = jjResolutionWidth + 5;
float r = deathCircle.r;
Point c = deathCircle.c;
/* Draw circle using horizontal rectangles,
we draw 600 horizontal rectangles instead of drawing 600 * 800 pixels,
which is much faster! */
for(int iY = 0; iY < height; iY++)
{
float recWidth;
float y = player.cameraY + iY;
float x0 = 0, x1 = 0;
bool drawRec0, drawRec1;
/* Circle equation:
(x-xC)^2 + (y-yC)^2 = r^2;
Attempt to solve for x0,x1:
x0,1 = xC +- sqrt(r^2 - (y-yC)^2) */
float dRYSq = r * r - (y - c.y) * (y - c.y);
/* If this horizontal line is outside the circle, fill the entire width */
if(dRYSq < 0)
{
x0 = player.cameraX + width;
drawRec0 = true;
drawRec1 = false;
}
else /*This horizontal line intersects the circle in 2 points */
{
float dRY = sqrt(dRYSq);
x0 = c.x - dRY;
x1 = c.x + dRY;
drawRec0 = true;
drawRec1 = true;
}
/* Draw the rectangles:
Left-Camera-Edge|---------------x0 x1-----------------|Right-Camera-Edge */
if(drawRec0)
{
if(deathCircle.OutsideCircle(player.cameraX + jjResolutionWidth / 2, player.cameraY + jjResolutionHeight / 2))
jjDrawRectangle(player.cameraX, y, int(x0 - player.cameraX), 1, ChromaKeyIndex, SPRITE::TRANSLUCENT, 0, -10);
else
jjDrawRectangle(player.cameraX, y, int(x0 - player.cameraX), 1, ChromaKeyIndex, SPRITE::SINGLECOLOR, ChromaKeyIndex, LayerZ);
}
if(drawRec1)
{
if(deathCircle.OutsideCircle(player.cameraX + jjResolutionWidth / 2, player.cameraY + jjResolutionHeight / 2))
jjDrawRectangle(x1, y, int(player.cameraX + width - x1), 1, ChromaKeyIndex, SPRITE::TRANSLUCENT, 0 , -10);
else
jjDrawRectangle(x1, y, int(player.cameraX + width - x1), 1, ChromaKeyIndex, SPRITE::SINGLECOLOR, ChromaKeyIndex, LayerZ);
}
}
/*HURT IF OUTSIDE CIRCLE*/
if(deathCircle.OutsideCircle(player.xPos, player.yPos) && (jjGameState == GAME::STARTED || jjGameState == GAME::OVERTIME))
{
player.hurt();
}
}
enum packetCmd
{
COMMAND_NONE,
COMMAND_CLIENT_REQUEST_UPDATE_FROM_SERVER,
COMMAND_PLAYER_WANTS_BROADCAST
}
void PopCircleStats(jjSTREAM &in packet)
{
packet.pop(deathCircle.c.y);
packet.pop(deathCircle.c.x);
packet.pop(deathCircle.r);
packet.pop(deathCircle.rSpeed);
}
void PushCircleStats(jjSTREAM &inout packet)
{
packet.push(deathCircle.c.y);
packet.push(deathCircle.c.x);
packet.push(deathCircle.r);
packet.push(deathCircle.rSpeed);
}
void BroadcastCircleStats()
{
jjSTREAM packet;
if(jjIsServer)
{
packet.push(COMMAND_NONE);
}
else
{
packet.push(COMMAND_PLAYER_WANTS_BROADCAST);
}
PushCircleStats(packet);
jjSendPacket(packet);
}
void ClientRequestCircleStats()
{
jjSTREAM packet;
packet.push(COMMAND_CLIENT_REQUEST_UPDATE_FROM_SERVER);
jjSendPacket(packet);
}
void onReceive(jjSTREAM &in packet, int clientID)
{
int cmd;
packet.pop(cmd);
switch (cmd)
{
case COMMAND_CLIENT_REQUEST_UPDATE_FROM_SERVER:
if (jjIsServer)
{
BroadcastCircleStats();
}
break;
case COMMAND_PLAYER_WANTS_BROADCAST:
if (jjIsServer)
{
PopCircleStats(packet);
BroadcastCircleStats();
}
break;
case COMMAND_NONE:
if (!jjIsServer)
{
PopCircleStats(packet);
}
break;
default:
jjAlert("Error in packet command!");
break;
}
}
bool onLocalChat(string &in stringReceived, CHAT::Type chatType)
{
array<string> results;
if (jjRegexIsValid(stringReceived) && (jjIsAdmin || jjIsServer))
{
if (jjRegexMatch(stringReceived, "!deathcircle\\s+(.+)", results, true))
{
float circleCompletionTime = parseFloat(results[1]);
if(circleCompletionTime != 0.f)
{
jjAlert("DeathCircle timelimit has been set to " + circleCompletionTime + " minutes", true);
deathCircle.UpdateSpeed(circleCompletionTime * 60);
BroadcastCircleStats();
return true;
}
}
}
return false;
}
void onLevelBegin()
{
jjAlert("ADMINS: type !deathcircle <time_m> to change DeathCircle timelimit for this match, default is 2");
deathCircle = DeathCircle();
if(!jjIsServer)
{
ClientRequestCircleStats();
}
}
void onMain()
{
if(jjGameState == GAME::STARTED || jjGameState == GAME::OVERTIME)
{
deathCircle.UpdateCircle();
}
}
Jazz2Online © 1999-INFINITY (Site Credits). We have a Privacy Policy. Jazz Jackrabbit, Jazz Jackrabbit 2, Jazz Jackrabbit Advance and all related trademarks and media are ™ and © Epic Games. Lori Jackrabbit is © Dean Dodrill. J2O development powered by Loops of Fury and Chemical Beats.
Eat your lima beans, Johnny.