/* ////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// 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 _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 _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; } } }