ScarletBlackMarket/script/KevinSystem/Universal_EnemyLib.dnh

905 lines
25 KiB
Plaintext
Raw Normal View History

2022-08-20 05:42:00 +00:00
/*
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
UNIVERSAL LIBRARY FOR ENEMY-RELATED FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
*/
// Code by Kevinmonitor with some assistance.
/*
///////////// ENEMY SPAWNING DURING STAGES /////////////
*/
task _InitDifficulty(int diffInt){
if(GetCommonData("Difficulty", "Normal") == "Normal"){
diffInt = 0;
}
else if(GetCommonData("Difficulty", "Normal") == "Hyper"){
diffInt = 1;
}
else if(GetCommonData("Difficulty", "Normal") == "Unparalleled"){
diffInt = 2;
}
return;
}
// Explosion Sound Effects
task _RenderBoss(int enemy){
int aniidle = 0;
string tex = "script/game/StageLib/BossAsset.png";
LoadTextureEx(tex, true, true);
ObjPrim_SetTexture(enemy, tex);
ObjSprite2D_SetSourceRect(enemy, 0, 0, 512, 512);
ObjSprite2D_SetDestCenter(enemy);
ObjRender_SetScaleXYZ(enemy, 0.48);
int nameText = CreateTextObject(
GetStgFrameWidth()*0.38, 10, 42,
"BOSS: Chimata Tenkyuu", "Connecting Chain Handserif",
0x8ABFFF, 0xFFFFFF,
0x552A9C, 3,
1
);
ObjText_SetFontBold(nameText, true);
_BossMarker(enemy, tex, 512, 0, 768, 256, 0.45, 30);
while(!Obj_IsDeleted(enemy)){
yield;
}
}
task _RenderMidboss(int enemy){
int aniidle = 0;
string tex = "script/game/StageLib/BossAsset.png";
LoadTextureEx(tex, true, true);
int nameText = CreateTextObject(
GetStgFrameWidth()*0.38, 10, 42,
"MIDBOSS: Kobiko", "Connecting Chain Handserif",
0xBA8AFF, 0xFFFFFF,
0x552A9C, 3,
1
);
ObjText_SetFontBold(nameText, true);
ObjPrim_SetTexture(enemy, tex);
ObjSprite2D_SetSourceRect(enemy, 768, 0, 768+512, 512);
ObjSprite2D_SetDestCenter(enemy);
ObjRender_SetScaleXYZ(enemy, 0.5);
_BossMarker(enemy, tex, 512, 256, 768, 512, 0.45, 30);
while(!Obj_IsDeleted(enemy)){
yield;
}
}
task _FadeInSpawn(
int enemyID,
float spawnX, float spawnY,
float destX, float destY,
float spawnScale, float destScale,
int time){
ObjMove_SetPosition(enemyID, spawnX, spawnY);
ObjRender_SetAlpha(enemyID, 0);
ObjMove_SetDestAtFrame(enemyID, destX, destY, time, LERP_DECELERATE);
ascent(i in 0..time){
ObjRender_SetAlpha(enemyID, Interpolate_Decelerate(0, 255, i/time));
ObjRender_SetScaleXYZ(enemyID, Interpolate_Decelerate(spawnScale, destScale, i/time));
yield;
}
}
int itemScript = LoadScriptInThread("script/KevinSystem/kevin_system/KevinSystem_Item.txt");
StartScript(itemScript);
// Creates an enemy and spawns them using _FadeInSpawn. Shooting and movement tasks not covered.
// Task also handles deleting the enemy after their life reaches 0.
/*
BhestieBlue: 0, 0, 256, 256
BhestieGreen: 256, 0, 512, 256
GaySamoyed: 0, 256, 256, 512
EvilSamoyed: 256, 256, 512, 512
*/
function <int> _CreateEnemy(
float spawnX, float spawnY, float destX, float destY, int spawntime,
float spawnScale, float destScale,
float health, float sizeHitbox, float sizeHurtbox,
texture,
int rectLeft, int rectTop, int rectRight, int rectBottom){
// Essentials
int enemyID = ObjEnemy_Create(OBJ_ENEMY);
ObjEnemy_Regist(enemyID);
ObjEnemy_SetLife(enemyID, health);
ObjEnemy_SetAutoDelete(enemyID, true);
// TBA: Handle animations
ObjPrim_SetTexture(enemyID, texture);
ObjSprite2D_SetSourceRect(enemyID, rectLeft, rectTop, rectRight, rectBottom);
ObjSprite2D_SetDestCenter(enemyID);
// Spawning
_FadeInSpawn(enemyID, spawnX, spawnY, destX, destY, spawnScale, destScale, spawntime);
_HandleEnemyWellbeing(enemyID, sizeHitbox, sizeHurtbox);
return enemyID;
}
// Hitbox and deletion
task _HandleEnemyWellbeing(int enemyID, float sizeHitbox, float sizeHurtbox){
while(ObjEnemy_GetInfo(enemyID, INFO_LIFE) > 0){
ObjEnemy_SetIntersectionCircleToShot(enemyID, ObjMove_GetX(enemyID), ObjMove_GetY(enemyID), sizeHitbox);
ObjEnemy_SetIntersectionCircleToPlayer(enemyID, ObjMove_GetX(enemyID), ObjMove_GetY(enemyID), sizeHurtbox);
yield;
}
Obj_Delete(enemyID);
}
// Fading invincibility for bosses/enemies (with parameters for wait times and fade times, and a separate multiplier for bomb damage rate (final spells))
task _FadeInvincibility(int target, int waittime, int fadetime, float bombmultiplier){
float x = 0;
ObjEnemy_SetDamageRate(target, 0, 0);
wait(waittime);
ascent(i in 0..fadetime){
x = Interpolate_Accelerate(0, 100, i/fadetime);
ObjEnemy_SetDamageRate(target, x, x*bombmultiplier);
yield;
}
}
/*
///////////// HANDLING THE END OF SINGLES (DURING BOSS/MIDBOSS FIGHTS) /////////////
To-do: Move item creation to the item script(s)
*/
////// NEW //////
task _GoToBrazil(
int targetscene, targetenemy,
int maxitemdrop, int minitemdrop){
while(ObjEnemy_GetInfo(targetenemy, INFO_LIFE)>0){
yield;
}
maxitemdrop = 14;
minitemdrop = 7;
int finalItemDrop = 0;
let itemType = POINT_RAINBOW;
int maxTimer = ObjEnemyBossScene_GetInfo(targetscene, INFO_ORGTIMERF);
int killTimer = ObjEnemyBossScene_GetInfo(targetscene, INFO_TIMERF);
bool isnon = ObjEnemyBossScene_GetInfo(targetscene, INFO_IS_SPELL);
// If the boss is killed within the first 1/5 of the timer, drop full items
if(killTimer >= maxTimer*(4/5)) {finalItemDrop = maxitemdrop;}
// Starting from the rest of the timer, the maximum items that can be dropped is equal to 4/5 of the max drop amount
else{finalItemDrop = Interpolate_Decelerate(minitemdrop, maxitemdrop*(4/5), killTimer/(maxTimer*4/5));}
// If the player has lost a life, lock the number of items to the lowest possible
if(ObjEnemyBossScene_GetInfo(targetscene, INFO_PLAYER_SHOOTDOWN_COUNT) > 0){
finalItemDrop = minitemdrop;
itemType = POINT_REGULAR;
}
DeleteShotAll(TYPE_ALL,TYPE_ITEM);
SetPlayerInvincibilityFrame(120);
// Common Data
NotifyEventAll(EV_SINGLE_ITEM_DROP, ObjMove_GetX(targetenemy), ObjMove_GetY(targetenemy), finalItemDrop, itemType);
wait(120);
float[] enemyPos = [ObjMove_GetX(targetenemy), ObjMove_GetY(targetenemy)];
SetCommonData("Entering PIV", GetAreaCommonData("PIV", "currentvalue", 10000));
SetCommonData("Boss Position X", enemyPos[0]);
SetCommonData("Boss Position Y", enemyPos[1]);
Obj_Delete(targetenemy);
}
////// OLD/LEGACY //////
// Basic post-death handling after every attack (Duo).
function _GoToBrazilDuo(targetscene, targetenemy, targetenemyA, targetenemyB, returnXA, returnYA, returnXB, returnYB, bool isnon, int itemdropamount){
while(ObjEnemy_GetInfo(targetenemy, INFO_LIFE) > 0){
yield;
}
DeleteShotAll(TYPE_ALL,TYPE_ITEM);
SetPlayerInvincibilityFrame(120);
// returnX and returnY are the boss positions they return to at the end of every pattern. (I'm too lazy for common data...)
ObjMove_CancelMovement(targetenemyA); ObjMove_CancelMovement(targetenemyB);
ObjMove_SetDestAtFrame(targetenemyA, returnXA, returnYA, 45, LERP_DECELERATE);
ObjMove_SetDestAtFrame(targetenemyB, returnXB, returnYB, 45, LERP_DECELERATE);
async{
wait(45);
ObjMove_CancelMovement(targetenemyA); ObjMove_CancelMovement(targetenemyB);
ObjMove_SetDestAtFrame(targetenemyA, returnXA, returnYA, 15, LERP_DECELERATE);
ObjMove_SetDestAtFrame(targetenemyB, returnXB, returnYB, 15, LERP_DECELERATE);
ObjMove_SetProcessMovement(targetenemyA, false);
ObjMove_SetProcessMovement(targetenemyB, false);
return;
}
alternative(isnon)
case(true){_NonspellItemDrop(targetscene, targetenemyA, itemdropamount); _NonspellItemDrop(targetscene, targetenemyB, itemdropamount);}
others{_SpellItemDrop(targetscene, targetenemyA, itemdropamount); _SpellItemDrop(targetscene, targetenemyB, itemdropamount);}
wait(120);
Obj_Delete(targetenemy);
Obj_Delete(targetenemyA);
Obj_Delete(targetenemyB);
}
// Basic post-death handling after every attack (Solo).
task _GoToBrazil(targetscene, targetenemy, returnX, returnY, bool isnon, int itemdropamount){
while(ObjEnemy_GetInfo(targetenemy, INFO_LIFE)>0){
yield;
}
DeleteShotAll(TYPE_ALL,TYPE_ITEM);
SetPlayerInvincibilityFrame(120);
// ITS COMMON DATA TIME BITCHES
float[] enemyPos = [ObjMove_GetX(targetenemy), ObjMove_GetY(targetenemy)];
SetCommonData("Boss Position", enemyPos);
alternative(isnon)
case(true){_NonspellItemDrop(targetscene, targetenemy, itemdropamount);}
others{_NonspellItemDrop(targetscene, targetenemy, itemdropamount);}
wait(120);
Obj_Delete(targetenemy);
}
task _GoToBrazilMidboss(targetscene, targetenemy, bool isnon, int itemdropamount){
while(ObjEnemy_GetInfo(targetenemy, INFO_LIFE)>0){
yield;
}
DeleteShotAll(TYPE_ALL,TYPE_ITEM);
SetPlayerInvincibilityFrame(120);
// ITS COMMON DATA TIME BITCHES
float[] enemyPos = [ObjMove_GetX(targetenemy), ObjMove_GetY(targetenemy)];
SetCommonData("Boss Position", enemyPos);
alternative(isnon)
case(true){_NonspellItemDrop(targetscene, targetenemy, itemdropamount);}
others{_NonspellItemDrop(targetscene, targetenemy, itemdropamount);}
wait(60);
ObjMove_SetDestAtFrame(targetenemy, enemyPos[0], -150, 45, LERP_SMOOTHER);
wait(45);
Obj_Delete(targetenemy);
}
// Used after end of last spell for dramatic effect. NOTE TO SELF: REPLACE SOUND & GRAPHICAL EFFECTS
task _ByeBitch(targetscene, targetenemy){
while(ObjEnemyBossScene_GetInfo(targetscene, INFO_CURRENT_LIFE) > 0){
yield;
}
/*ObjSound_Load(death, defeatsnd);
ObjSound_SetVolumeRate(death, 30);
ObjSound_SetFade(death, 5);*/
let x = ObjMove_GetX(targetenemy);
let y = ObjMove_GetY(targetenemy);
StartSlow(TARGET_ALL, 30);
SetPlayerInvincibilityFrame(180);
DeleteShotAll(TYPE_ALL,TYPE_ITEM);
//ObjSound_Play(death);
TExplosionA(x,y,10,0.5);
wait(120);
StopSlow(TARGET_ALL);
//ObjSound_Play(death);
//Obj_Delete(target);
TExplosionA(x,y,10,0.5);
wait(120);
}
function <void> _ByeBitches(int targetscene, int targetboss, int targetenemyA, int targetenemyB, int itemdropamount){
//wait(120);
loop{
float life = ObjEnemy_GetInfo(targetboss, INFO_LIFE);
if(life > 0){yield;}
else{break;}
}
int death = ObjSound_Create();
ObjSound_Load(death, defeatsnd);
ObjSound_SetVolumeRate(death, 40);
ObjSound_SetFade(death, 7.5);
StartSlow(TARGET_ALL, 30);
SetPlayerInvincibilityFrame(180);
DeleteShotAll(TYPE_ALL,TYPE_ITEM);
ObjSound_Play(death);
TExplosionA(ObjMove_GetX(targetenemyA), ObjMove_GetY(targetenemyA), 10, 0.5);
TExplosionA(ObjMove_GetX(targetenemyB), ObjMove_GetY(targetenemyB), 10, 0.5);
wait(120);
_SpellItemDrop(targetscene, targetenemyA, itemdropamount);
_SpellItemDrop(targetscene, targetenemyB, itemdropamount);
StopSlow(TARGET_ALL);
ObjSound_Play(death);
TExplosionA(ObjMove_GetX(targetenemyA), ObjMove_GetY(targetenemyA), 10, 0.5);
TExplosionA(ObjMove_GetX(targetenemyB), ObjMove_GetY(targetenemyB), 10, 0.5);
Obj_Delete(targetboss);
Obj_Delete(targetenemyA);
Obj_Delete(targetenemyB);
wait(120);
}
/*
//////////////// GRAPHICAL/VISUAL-RELATED TASKS AND FUNCTIONS ////////////////
*/
// Handles boss markers during boss fights.
task _BossMarker(int targetenemy, markertex, int left, int top, int right, int bottom, float scale, float heightoffset){
int marker = ObjPrim_Create(OBJ_SPRITE_2D);
ObjPrim_SetTexture(marker, markertex);
ObjSprite2D_SetSourceRect(marker, left, top, right, bottom);
ObjSprite2D_SetDestCenter(marker);
ObjRender_SetScaleXYZ(marker, scale);
Obj_SetRenderPriorityI(marker, 19);
ObjRender_SetAlpha(marker, 255);
//ObjRender_SetY(marker, GetStgFrameHeight()+heightoffset);
while(!Obj_IsDeleted(targetenemy)){
ObjRender_SetPosition(marker, ObjRender_GetX(targetenemy)+(GetScreenWidth()-GetStgFrameWidth())/2, GetStgFrameHeight()+heightoffset+(GetScreenHeight()-GetStgFrameHeight())/2, 1);
yield;
}
Obj_Delete(marker);
}
// Tasks that handle calling a spellcard. No spell card history.
task _DuoCutin(
image1, image2,
int rectleft, int recttop, int rectright, int rectbottom,
int spellID, string spellname,
float textsize, float bordersize, int bordercolor, int bonusbordercolor,
float textstartx, float textendx, int render1, int render2
){
// Handle the appearance and disappearances of the images
// 0 = upwards, 1 = downwards || 0 = left, 1 = right
// Handle the spell text
int spelltext = ObjText_Create();
ObjText_SetText(spelltext, spellname);
ObjText_SetFontType(spelltext, "Connecting Chain Handserif");
ObjText_SetFontSize(spelltext, textsize);
ObjText_SetFontColorTop(spelltext, 255, 255, 255);
ObjText_SetFontColorBottom(spelltext, 255, 255, 255);
ObjText_SetFontBorderType(spelltext, BORDER_FULL);
ObjText_SetFontBorderColor(spelltext, bordercolor);
ObjText_SetFontBorderWidth(spelltext, bordersize);
ObjText_SetHorizontalAlignment(spelltext, ALIGNMENT_RIGHT);
ObjText_SetMaxWidth(spelltext, GetStgFrameWidth);
Obj_SetRenderPriorityI(spelltext, 79);
ObjRender_SetPosition(spelltext, textstartx, GetStgFrameHeight/8-10, 0); // WIP
ObjRender_SetAlpha(spelltext, 255);
// Handle the spell bonus
let objBonus = ObjText_Create();
ObjText_SetFontSize(objBonus, 38);
ObjText_SetFontBold(objBonus, true);
ObjText_SetFontType(objBonus, "Anke Calligraph");
ObjText_SetFontColorTop(objBonus, 255, 255, 255);
ObjText_SetFontColorBottom(objBonus, 255, 255, 255);
ObjText_SetFontBorderType(objBonus, BORDER_FULL);
ObjText_SetFontBorderColor(objBonus, bonusbordercolor);
ObjText_SetFontBorderWidth(objBonus, 2);
ObjText_SetHorizontalAlignment(objBonus, ALIGNMENT_RIGHT);
ObjText_SetMaxWidth(objBonus, GetStgFrameWidth-24);
Obj_SetRenderPriorityI(objBonus, 79);
ObjRender_SetPosition(objBonus, textendx, GetStgFrameHeight/8+12, 0); // WIP
ObjRender_SetAlpha(objBonus, 255);
async{
while(ObjEnemyBossScene_GetInfo(spellID, INFO_PLAYER_SHOOTDOWN_COUNT) == 0 && ObjEnemyBossScene_GetInfo(spellID, INFO_PLAYER_SPELL_COUNT) == 0)
{
int score = trunc(ObjEnemyBossScene_GetInfo(spellID, INFO_SPELL_SCORE)/10) * 10;
score = min(score, 9999999990);
let yass = DigitToCommaArray(score);
ObjText_SetText(objBonus, yass);
yield;
}
ObjText_SetText(objBonus, ":(");
}
// Actually call the tasks and functions here
// Mm
int objCutin1 = _Create2DImage(image1, rectleft, recttop, rectright, rectbottom);
int objCutin2 = _Create2DImage(image2, rectleft, recttop, rectright, rectbottom);
Obj_SetRenderPriorityI(objCutin1, render1);
Obj_SetRenderPriorityI(objCutin2, render2);
_MoveVertical(objCutin1, 0, 0);
_MoveVertical(objCutin2, 1, 1);
//WriteLog(ObjRender_GetY(objCutin1));
//_MoveIntoPlace(spelltext, textstartx, textendx);
_FadeAtPlayer(spelltext);
_FadeAtPlayer(objBonus);
while(ObjEnemyBossScene_GetInfo(spellID, INFO_CURRENT_LIFE) > 0){yield;}
Obj_Delete(spelltext);
Obj_Delete(objBonus);
//return;
}
task _SoloCutin(
image,
int rectleft, int recttop, int rectright, int rectbottom,
int bossID, int spellID, string spellname,
float textsize, float bordersize, int bordercolor, int bonusbordercolor,
float textstartx, float texty, int render
){
// Handle the spell text
int spelltext = ObjText_Create();
ObjText_SetText(spelltext, spellname);
ObjText_SetFontType(spelltext, "Connecting Chain Handserif");
ObjText_SetFontSize(spelltext, textsize);
ObjText_SetFontColorTop(spelltext, 255, 255, 255);
ObjText_SetFontColorBottom(spelltext, 255, 255, 255);
ObjText_SetFontBorderType(spelltext, BORDER_FULL);
ObjText_SetFontBorderColor(spelltext, bordercolor);
ObjText_SetFontBorderWidth(spelltext, bordersize);
ObjText_SetHorizontalAlignment(spelltext, ALIGNMENT_LEFT);
//ObjText_SetMaxWidth(spelltext, GetStgFrameWidth);
Obj_SetRenderPriorityI(spelltext, 79);
ObjRender_SetPosition(spelltext, textstartx, texty, 0); // WIP
ObjRender_SetAlpha(spelltext, 255);
// Handle the spell bonus
let objBonus = ObjText_Create();
ObjText_SetFontSize(objBonus, textsize*1.25);
ObjText_SetFontBold(objBonus, true);
ObjText_SetFontType(objBonus, "Anke Calligraph");
ObjText_SetFontColorTop(objBonus, 255, 255, 255);
ObjText_SetFontColorBottom(objBonus, 255, 255, 255);
ObjText_SetFontBorderType(objBonus, BORDER_FULL);
ObjText_SetFontBorderColor(objBonus, bonusbordercolor);
ObjText_SetFontBorderWidth(objBonus, 2);
ObjText_SetHorizontalAlignment(objBonus, ALIGNMENT_LEFT);
//ObjText_SetMaxWidth(objBonus, GetStgFrameWidth-24);
Obj_SetRenderPriorityI(objBonus, 79);
ObjRender_SetPosition(objBonus, textstartx, texty+textsize-5, 0); // WIP
ObjRender_SetAlpha(objBonus, 255);
async{
while(ObjEnemyBossScene_GetInfo(spellID, INFO_PLAYER_SHOOTDOWN_COUNT) == 0 && ObjEnemyBossScene_GetInfo(spellID, INFO_PLAYER_SPELL_COUNT) == 0)
{
int score = trunc(ObjEnemyBossScene_GetInfo(spellID, INFO_SPELL_SCORE)/10) * 10;
score = min(score, 9999999990);
let yass = DigitToCommaArray(score);
ObjText_SetText(objBonus, "Bonus: " ~ yass);
yield;
}
ObjText_SetText(objBonus, "Bonus Failed...");
}
// Actually call the tasks and functions here
// Mm
int objCutin = _Create2DImage(image, rectleft, recttop, rectright, rectbottom);
Obj_SetRenderPriorityI(objCutin, render);
_MoveDiagonal(objCutin, GetStgFrameWidth()*1.25, -120, GetStgFrameWidth()/2, GetStgFrameHeight()/2, -120, GetStgFrameHeight()*1.25);
//WriteLog(Obj_IsVisible(spelltext));
//_MoveIntoPlace(spelltext, textstartx, textendx);
_FadeAtPlayer(spelltext);
_FadeAtPlayer(objBonus);
while(ObjEnemyBossScene_GetInfo(spellID, INFO_CURRENT_LIFE) > 0){yield;}
Obj_Delete(spelltext);
Obj_Delete(objBonus);
//return;
}
function DigitToCommaArray(num){ //Natashinitai
let srcStr = IntToString(num);
let res = [];
let nChar = 0;
for(let i = length(srcStr) - 1; i >= 0; i--){
res = [srcStr[i]] ~ res;
nChar++;
if(nChar % 3 == 0 && i > 0) res = "," ~ res;
}
return res;
}
// Tasks meant to be used with cutin tasks.
task _MoveVertical(int target, int move, int widthposition){
float positionstart = [GetStgFrameHeight()*1.5, -GetStgFrameHeight()][move];
float positionmid = GetStgFrameHeight()/2;
float positionend = [-GetStgFrameHeight()*1.5, GetStgFrameHeight()*1.5][move];
float positionhort = [3*GetStgFrameWidth/8, 6*GetStgFrameWidth/8][widthposition];
ObjRender_SetPosition(target, positionhort, positionstart, 0);
ascent(i in 0..30){
ObjRender_SetY(target, Interpolate_Decelerate(positionstart, positionmid, i/30));
yield;
}
wait(30);
ascent(i in 0..30){
ObjRender_SetY(target, Interpolate_Accelerate(positionmid, positionend, i/30));
yield;
}
Obj_Delete(target);
return;
}
task _MoveDiagonal(int target, int startx, int starty, int midx, int midy, int endx, int endy){
//"
ObjRender_SetPosition(target, startx, endx, 1);
ascent(i in 0..30){
ObjRender_SetPosition(target, Interpolate_Decelerate(startx, midx, i/30), Interpolate_Decelerate(starty, midy, i/30), 1);
ObjRender_SetAlpha(target, Interpolate_Decelerate(0, 255, i/30));
yield;
}
wait(30);
ascent(i in 0..30){
ObjRender_SetPosition(target, Interpolate_Decelerate(midx, endx, i/30), Interpolate_Decelerate(midy, endy, i/30), 1);
ObjRender_SetAlpha(target, Interpolate_Decelerate(255, 0, i/30));
yield;
}
return;
}
task _MoveIntoPlace(int target, int startx, int endx){
ascent(i in 0..30){
ObjRender_SetX(target, Interpolate_Decelerate(startx, endx, i/30));
ObjRender_SetAlpha(target, Interpolate_Decelerate(0, 255, i/30));
yield;
}
return;
}
task _FadeAtPlayer(int target){
wait(30);
while(!Obj_IsDeleted(target)){
int player = GetPlayerObjectID();
if(ObjRender_GetY(player) <= GetStgFrameHeight()/3.5){ObjRender_SetAlpha(target, 60);}
else{ObjRender_SetAlpha(target, 255);}
yield;
}
return;
}
// Enemy render tasks
/*
These render tasks assume the order of the spritesheet goes like this:
Idle Idle2 Idle3 ...
Left Left2 Left3 ...
Right
*/
task _RenderEnemyMovement(int obj, int frame, int offsetleft, int offsettop, int width, int height, float flipscale, int frameno, int speed){
ObjSprite2D_SetSourceRect(obj, offsetleft+width*floor(frame/speed), offsettop, width+width*floor(frame/speed), offsettop+height);
ObjRender_SetScaleX(obj, flipscale);
}
task _EnemyRenderFull(int enemyobj, int texture, int offsetleft, int offsettop, int width, int height, int framenoidle, int framenomovement, int speed){
int aniidle = 0;
int animove = 0;
while(!Obj_IsDeleted(enemyobj)){
float dir = ObjMove_GetAngle(enemyobj);
float speed = ObjMove_GetSpeed(enemyobj);
// Idle
if (speed == 0){
_RenderEnemyMovement(enemyobj, aniidle, offsetleft, offsettop, width, height, 1, framenoidle, speed);
aniidle++;
if(aniidle >= speed*framenoidle){aniidle = 0;}
}
// Left
else if (cos(dir) < 0){
_RenderEnemyMovement(enemyobj, aniidle, offsetleft, offsettop+height, width, height*2, 1, framenomovement, speed);
animove++;
if(animove >= speed*framenomovement){animove = 0;}
}
// Right
else if (cos(dir) > 0){
_RenderEnemyMovement(enemyobj, aniidle, offsetleft, offsettop+height*2, width, height*3, 1, framenomovement, speed);
animove++;
if(animove >= speed*framenomovement){animove = 0;}
}
yield;
}
}
// Enemy deletion/explosion effects.
// This particle list is used for all instances of the effect.
task _EffectListPreRender(
int targetList,
texture, int[] rect
){
ObjPrim_SetTexture(targetList, texture);
Obj_SetRenderPriorityI(targetList, 39);
ObjPrim_SetPrimitiveType(targetList, PRIMITIVE_TRIANGLELIST);
ObjPrim_SetVertexCount(targetList, 4);
ObjRender_SetBlendType(targetList, BLEND_ALPHA);
// Left-top, right-top, left-bottom, right-bottom
ObjPrim_SetVertexUVT(targetList, 0, rect[0], rect[1]);
ObjPrim_SetVertexUVT(targetList, 1, rect[2], rect[1]);
ObjPrim_SetVertexUVT(targetList, 2, rect[0], rect[3]);
ObjPrim_SetVertexUVT(targetList, 3, rect[2], rect[3]);
// Vertex positions are offset with deltas so that the sprite is centered
float dU = (rect[2] - rect[0])/2;
float dV = (rect[3] - rect[1])/2;
ObjPrim_SetVertexPosition(targetList, 0, -dU, -dV, 1);
ObjPrim_SetVertexPosition(targetList, 1, dU, -dV, 1);
ObjPrim_SetVertexPosition(targetList, 2, -dU, dV, 1);
ObjPrim_SetVertexPosition(targetList, 3, dU, dV, 1);
ObjPrim_SetVertexIndex(targetList, [0, 1, 2, 1, 2, 3]);
}
// Drop a number of point items and petals depending on how fast you kill the enemy and how close you are to them.
task _EnemyItemDrop(
int ID,
int minPoint, int minPIV,
int maxPoint, int maxPIV,
int maxTimer, int maxDist
){
//wait(spawnTime+5);
float enmX = ObjMove_GetX(ID), enmY = ObjMove_GetY(ID);
int Bitch = ID;
float timer = 1;
float dmgCheck = 0;
ObjEnemy_SetDamageRate(Bitch, 100, 100);
while(ObjEnemy_GetInfo(Bitch, INFO_LIFE) > 0){
enmX = ObjMove_GetX(Bitch);
enmY = ObjMove_GetY(Bitch);
dmgCheck = ObjEnemy_GetInfo(Bitch, INFO_DAMAGE_PREVIOUS_FRAME);
timer++;
yield;
}
if(
(-96 < enmX && enmX < STG_WIDTH+96)
&&
(-96 < enmY && enmY < STG_HEIGHT+96)
)
{
_ExplosionEffect(enmX, enmY, PetalEffect);
//_ScorePopup(float enmX, float enmY, int pointNum, int pivNum);
// NotifyEvent is faster than NotifyEventAll, considering how the item event will be called many times.
NotifyEvent(itemScript, EV_DROP_POINT_ENEMY, [enmX, enmY], timer, maxTimer, minPoint, maxPoint);
NotifyEvent(itemScript, EV_DROP_PIV_ENEMY, GetPlayerObjectID(), [enmX, enmY], minPIV, maxPIV, maxDist);
}
}
// Enemy go Boom
/*
5 flower petals appear from the enemy and spin outwards.
Their angle constantly changes and their alpha lowers over time.
*/
task _ExplosionEffect(float enmX, float enmY, int targetList){
int i = 0;
int effectLength = 45;
int effectNum = 5;
int dir = 1;
TExplosionA(enmX, enmY, 255/effectLength, 9/effectLength);
/*ascent(i in 0..effectNum){
_CreatePetal(rand(5, 10)*dir, rand(5, 10)*dir, rand(0, 360));
dir *= -1;
//_CreatePetal(rand(50, 80), rand(-80, -50), rand(0, 360));
}*/
_CreatePetal(rand(8, 11), rand(-8, -11), rand(0, 360));
_CreatePetal(rand(-11, -8), rand(-2, 2), rand(0, 360));
_CreatePetal(rand(8, 8), rand(-2, 2), rand(0, 360));
_CreatePetal(rand(-11, -8), rand(8, 11), rand(0, 360));
_CreatePetal(rand(8, 11), rand(8, 11), rand(0, 360));
ObjSound_Play(sfxBoom);
// Create a petal with:
// x offset every frame, y offset every frame
task _CreatePetal(float spdX, float spdY, float baseAng){
float x = enmX, y = enmY;
let x_speed = spdX;
let y_speed = spdY;
let z_add = rand(-5, 5);
ascent(i in 0..effectLength){
_PetalMovement(Interpolate_Decelerate(1.5, 0.5, i/effectLength), Interpolate_Decelerate(255, 0, i/effectLength));
yield;
}
task _PetalMovement(scale, alpha){
ObjParticleList_SetScale(targetList, scale);
ObjParticleList_SetAngleZ(targetList, baseAng);
ObjParticleList_SetPosition(targetList, x, y, 1);
ObjParticleList_SetAlpha(targetList, alpha);
//Submits the current data to an instance, cleared every frame.
ObjParticleList_AddInstance(targetList);
x += x_speed;
y += y_speed;
baseAng += z_add;
yield;
}
}
}