470 lines
10 KiB
Plaintext
470 lines
10 KiB
Plaintext
#TouhouDanmakufu[Single]
|
|
#ScriptVersion[3]
|
|
#Title["Remilia Spell 2 (Patchouli Card)"]
|
|
#Text["yassification"]
|
|
#System["script/KevinSystem/Kevin_System.txt"]
|
|
|
|
int difficultySelect = 0;
|
|
|
|
let objScene = GetEnemyBossSceneObjectID();
|
|
let csd = GetCurrentScriptDirectory();
|
|
|
|
let bossObj;
|
|
|
|
float bossX = 0;
|
|
float bossY = 0;
|
|
float playerY = 0;
|
|
float playerX = 0;
|
|
|
|
let aniframe = 0;
|
|
let aniframe2 = 0;
|
|
|
|
let spellsnd = ObjSound_Create();
|
|
let bossdiesnd = ObjSound_Create();
|
|
|
|
/*
|
|
|
|
Parameters:
|
|
|
|
- Line density
|
|
- Line speed
|
|
- Line delay
|
|
|
|
- Ring density
|
|
- Ring max speed
|
|
- Delays between rings
|
|
- Remilia charge time
|
|
|
|
- Aim speed
|
|
|
|
*/
|
|
|
|
int[] denseLine = [3, 4, 5];
|
|
int[] speedLine = [8.5, 10, 12];
|
|
float[] delayLine = [23, 20, 18];
|
|
|
|
int[] denseRing = [10, 12, 14];
|
|
float[] maxspeedRing = [8, 9, 11];
|
|
int[] deceltimeRing = [20, 30, 40];
|
|
int[] acceltimeRing = [60, 45, 35];
|
|
|
|
// Remilia takes 36 frames to charge down the screen. Preferably, pick a number 36 can divide with.
|
|
|
|
int[] ringDelay = [12, 9, 6];
|
|
|
|
// How much time Remilia takes to charge her attack and you get to GTFO.
|
|
|
|
int[] chargetimeRemi = [75, 60, 50];
|
|
float[] maxspeedBookAim = [10, 12, 15];
|
|
|
|
int[] bookArray = []; // it's a secret tool that will help us later
|
|
bool bookShooting = false; // it's a secret tool that will help us later
|
|
|
|
string tex = "script/game/resourceLib/Spritesheet_StationJam.png";
|
|
|
|
// Includes ahoy
|
|
|
|
#include "script/KevinSystem/Universal_Lib.txt" // The library to include all libraries :sans: :nail_care:
|
|
|
|
@Initialize {
|
|
|
|
//SetIntersectionVisualization(true);
|
|
|
|
SetAutoDeleteObject(true);
|
|
LoadTextureEx(tex, true, false);
|
|
|
|
_InitDifficulty(difficultySelect);
|
|
|
|
//difficultySelect = 0; // debug
|
|
|
|
SetShotAutoDeleteClip(64, 64, 64, 64);
|
|
|
|
if(!IsCommonDataAreaExists("PIV")){
|
|
CreateCommonDataArea("PIV");
|
|
SetAreaCommonData("PIV", "currentvalue", 10000);
|
|
}
|
|
else{}
|
|
|
|
// Create the boss object itself
|
|
bossObj = ObjEnemy_Create(OBJ_ENEMY_BOSS);
|
|
ObjEnemy_Regist(bossObj);
|
|
|
|
ObjMove_SetPosition(bossObj, GetCommonData("Boss Position X", STG_WIDTH/2), GetCommonData("Boss Position Y", STG_HEIGHT/2));
|
|
ObjEnemy_SetMaximumDamage(bossObj, 999);
|
|
|
|
_RenderBoss(bossObj);
|
|
|
|
//WriteLog(spellPIV);
|
|
WriteLog(ObjEnemyBossScene_GetInfo(objScene, INFO_SPELL_SCORE));
|
|
|
|
mainTask();
|
|
_FadeInvincibility(bossObj, 150, 150, 1);
|
|
endingnew();
|
|
}
|
|
|
|
@Event {
|
|
|
|
alternative(GetEventType())
|
|
|
|
case(EV_REQUEST_LIFE) {
|
|
SetScriptResult(2500);
|
|
}
|
|
|
|
case(EV_REQUEST_TIMER) {
|
|
SetScriptResult(40);
|
|
}
|
|
|
|
// Start firing from books
|
|
case(EV_USER+999){
|
|
bookShooting = true;
|
|
}
|
|
|
|
// Stop firing from books
|
|
case(EV_USER+1000){
|
|
bookShooting = false;
|
|
}
|
|
|
|
}
|
|
|
|
@MainLoop {
|
|
|
|
playerY = GetPlayerY();
|
|
playerX = GetPlayerX();
|
|
//The player position is ALWAYS UPDATED
|
|
|
|
bossX = ObjMove_GetX(bossObj);
|
|
bossY = ObjMove_GetY(bossObj);
|
|
|
|
ObjEnemy_SetIntersectionCircleToShot(bossObj, ObjMove_GetX(bossObj), ObjMove_GetY(bossObj), 180);
|
|
|
|
yield;
|
|
|
|
}
|
|
|
|
@Finalize {
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
Spell 2 (Patchouli):
|
|
|
|
Remilia summons 5 books (5 elements) (see Philosopher's Stone). These books move to the top of the screen and fire downwards lines that restrict horizontal movement.
|
|
Remilia constantly will charge down at the player's horizontal position, leaving behind red rings. The player must weave through the lines of the books.
|
|
After 3 charges, the books turn into bats that aim for the player, leaving trails of bullets in their wake. Remilia re-calls 5 books and then the pattern restarts.
|
|
|
|
Note: Books = Enemies with no registered intersection
|
|
|
|
Parameters:
|
|
|
|
- Line density
|
|
- Line speed
|
|
- Line delay
|
|
|
|
- Ring density
|
|
- Ring speed
|
|
- Delays between rings
|
|
- Remilia charge time
|
|
- Remilia charge delay
|
|
|
|
- Aim speed
|
|
|
|
*/
|
|
|
|
task mainTask {
|
|
|
|
ObjMove_SetDestAtFrame(bossObj, STG_WIDTH/2, STG_HEIGHT/2
|
|
, 45, LERP_DECELERATE);
|
|
wait(45);
|
|
|
|
_UseCard();
|
|
// Create 5 books
|
|
|
|
CreateBook([0, 0, 256, 256], STG_WIDTH*1/6, 0.65, KEV_LEAF_RED);
|
|
CreateBook([256, 0, 512, 256], STG_WIDTH*2/6, 0.65, KEV_LEAF_AQUA);
|
|
CreateBook([512, 0, 768, 256], STG_WIDTH*3/6, 0.65, KEV_LEAF_ORANGE);
|
|
CreateBook([768, 0, 1024, 256], STG_WIDTH*4/6, 0.65, KEV_LEAF_WHITE);
|
|
CreateBook([1024, 0, 1280, 256], STG_WIDTH*5/6, 0.65, KEV_LEAF_GREEN);
|
|
|
|
bookShooting = true;
|
|
|
|
// It takes 60 seconds for the books to start firing..
|
|
|
|
wait(60+delayLine[difficultySelect]*3);
|
|
|
|
// Repeat 3 times: Choose an area based on the player's position, charge down there while firing fireballs + rings, then return to same position.
|
|
|
|
while(ObjEnemy_GetInfo(bossObj, INFO_LIFE) > 0){
|
|
|
|
if(playerX <= STG_WIDTH/6 && playerY >= 0){
|
|
ChargeAttack(0.5*STG_WIDTH/6, 0);
|
|
}
|
|
|
|
else if(playerX <= 2*STG_WIDTH/6 && playerY >= STG_WIDTH/6){
|
|
ChargeAttack(1.5*STG_WIDTH/6, 1);
|
|
}
|
|
|
|
else if(playerX <= 3*STG_WIDTH/6 && playerY >= 2*STG_WIDTH/6){
|
|
ChargeAttack(2.5*STG_WIDTH/6, 2);
|
|
}
|
|
|
|
else if(playerX <= 4*STG_WIDTH/6 && playerY >= 3*STG_WIDTH/6){
|
|
ChargeAttack(3.5*STG_WIDTH/6, 3);
|
|
}
|
|
|
|
else if(playerX <= 5*STG_WIDTH/6 && playerY >= 4*STG_WIDTH/6){
|
|
ChargeAttack(4.5*STG_WIDTH/6, 4);
|
|
}
|
|
|
|
else{
|
|
ChargeAttack(5.5*STG_WIDTH/6, 5);
|
|
}
|
|
|
|
yield;
|
|
|
|
}
|
|
|
|
// Aim books at the player.
|
|
|
|
// Remove books from bookArray and then restart pattern.
|
|
|
|
}
|
|
|
|
function <void> _UseCard(){
|
|
|
|
// Remi spell
|
|
|
|
int cardImg = _Create2DImage(tex, [256, 256, 512, 512]);
|
|
|
|
ObjRender_SetPosition(cardImg, bossX, bossY, 0);
|
|
|
|
ascent(i in 0..30){
|
|
ObjRender_SetY(cardImg, Interpolate_Decelerate(bossY, bossY - 256, i/30));
|
|
ObjRender_SetAlpha(cardImg, Interpolate_Decelerate(0, 255, i/30));
|
|
yield;
|
|
}
|
|
|
|
wait(30);
|
|
|
|
ascent(i in 0..15){
|
|
ObjRender_SetAlpha(cardImg, Interpolate_Decelerate(255, 0, i/15));
|
|
yield;
|
|
}
|
|
|
|
Obj_Delete(cardImg);
|
|
|
|
}
|
|
|
|
/*
|
|
Fire: 0, 0, 256, 256
|
|
Water: 256, 0, 512, 256
|
|
Wood: 512, 0, 768, 256
|
|
Metal: 768, 0, 1024, 256
|
|
Earth: 1024, 0, 1280, 256
|
|
*/
|
|
|
|
function <void> ChargeAttack(float x, int colorselect){
|
|
|
|
ObjMove_SetDestAtFrame(bossObj, x, STG_HEIGHT/7, 30, LERP_DECELERATE);
|
|
wait(30);
|
|
|
|
_CreateCustomTelegraphLine(
|
|
x, 0,
|
|
90, 90,
|
|
3333, 3333,
|
|
0, STG_WIDTH/6,
|
|
0xDC7171, 220,
|
|
15, chargetimeRemi[difficultySelect], 15
|
|
);
|
|
|
|
wait(chargetimeRemi[difficultySelect]-30);
|
|
|
|
// MOVEEEEE BITCH
|
|
|
|
ObjMove_SetDestAtFrame(bossObj, x, STG_HEIGHT/9, 30, LERP_DECELERATE);
|
|
wait(25);
|
|
|
|
//ObjEnemy_SetDamageRate(bossObj, 0, 0);
|
|
ObjMove_SetDestAtFrame(bossObj, x, STG_HEIGHT*1.2, 36, LERP_DECELERATE);
|
|
|
|
float ang = GetAngleToPlayer(bossObj);
|
|
|
|
async{
|
|
loop(36){
|
|
loop(2){
|
|
int shot = CreateShotA1(bossX+rand(-STG_HEIGHT/12, STG_HEIGHT/12), bossY, 18, rand(269, 271), KEV_FIRE_RED, 5);
|
|
if(ObjEnemy_GetInfo(bossObj, INFO_LIFE) <= 0){Obj_Delete(shot);}
|
|
else{
|
|
_Delay(shot, 5);
|
|
_BulletRescale(shot, 0.85, true, 1);
|
|
Shoot2;
|
|
}
|
|
}
|
|
wait(1);
|
|
}
|
|
ObjEnemy_SetDamageRate(bossObj, 0, 0);
|
|
}
|
|
|
|
int color = [KEV_AURABALL_RED, KEV_AURABALL_AQUA, KEV_AURABALL_ORANGE, KEV_AURABALL_WHITE, KEV_AURABALL_GREEN, KEV_AURABALL_PINK][colorselect];
|
|
|
|
loop(36/ringDelay){
|
|
|
|
ascent(i in 0..denseRing[difficultySelect]){
|
|
int shot = CreateShotA2(bossX, bossY, maxspeedRing[difficultySelect], ang + 360/denseRing[difficultySelect] * i, -maxspeedRing[difficultySelect]/deceltimeRing[difficultySelect], 0, 0, color, 10);
|
|
if(ObjEnemy_GetInfo(bossObj, INFO_LIFE) <= 0){Obj_Delete(shot);}
|
|
else{
|
|
_Delay(shot, 10);
|
|
ObjMove_AddPatternA2(shot, 30, NO_CHANGE, NO_CHANGE, maxspeedRing[difficultySelect]/acceltimeRing[difficultySelect], maxspeedRing[difficultySelect], 0);
|
|
_BulletRescale(shot, 0.6, true, 1);
|
|
//Shoot1;
|
|
}
|
|
}
|
|
wait(ringDelay);
|
|
|
|
}
|
|
|
|
wait(45);
|
|
|
|
ObjMove_SetPosition(bossObj, STG_WIDTH/2, -256);
|
|
ObjMove_SetDestAtFrame(bossObj, STG_WIDTH/2, STG_HEIGHT/2, 30, LERP_DECELERATE);
|
|
|
|
wait(15);
|
|
ObjEnemy_SetDamageRate(bossObj, 100, 100);
|
|
|
|
wait(75);
|
|
|
|
}
|
|
|
|
task CreateBook(
|
|
int[] rect,
|
|
int destX,
|
|
float scaleEnm, int graphicBullet
|
|
){
|
|
|
|
int book = _CreateEnemy(
|
|
false,
|
|
bossX, bossY, destX, STG_HEIGHT/7.5, 60,
|
|
scaleEnm, scaleEnm,
|
|
99999, -1, -1,
|
|
tex,
|
|
rect[0], rect[1], rect[2], rect[3]
|
|
);
|
|
|
|
Obj_SetRenderPriorityI(book, 39);
|
|
|
|
ObjEnemy_SetEnableIntersectionPositionFetching(book, false); // so the lock-on shot doesn't home onto the books
|
|
bookArray ~= [book];
|
|
|
|
// Fire task
|
|
async{
|
|
|
|
wait(60);
|
|
|
|
while(!Obj_IsDeleted(book) && bookShooting){
|
|
|
|
loop(denseLine[difficultySelect]){
|
|
int shot = CreateShotA1(ObjMove_GetX(book), ObjMove_GetY(book), speedLine[difficultySelect], 90, graphicBullet, 15);
|
|
if(ObjEnemy_GetInfo(bossObj, INFO_LIFE) <= 0){Obj_Delete(shot);}
|
|
else{
|
|
_Delay(shot, 10);
|
|
_BulletRescale(shot, 0.85, true, 1);
|
|
Shoot1;
|
|
}
|
|
wait([3, 4, 4][difficultySelect]);
|
|
}
|
|
|
|
wait(delayLine[difficultySelect]);
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
async{
|
|
while(ObjEnemy_GetInfo(bossObj, INFO_LIFE) > 0){
|
|
yield;
|
|
}
|
|
Obj_Delete(book);
|
|
|
|
}
|
|
|
|
async{
|
|
|
|
wait(60);
|
|
|
|
while(bookShooting){
|
|
|
|
ascent(i in 0..24){
|
|
int shot = CreateShotA1(ObjMove_GetX(book), ObjMove_GetY(book), 12, Interpolate_Smoother(190, 350, i/24), graphicBullet+44, 15);
|
|
if(ObjEnemy_GetInfo(bossObj, INFO_LIFE) <= 0){Obj_Delete(shot);}
|
|
else{
|
|
_Delay(shot, 0);
|
|
_BulletRescale(shot, 0.5, true, 1);
|
|
}
|
|
}
|
|
|
|
wait(delayLine[difficultySelect]);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
function <int> CreateFan (int parent, int way, int graphic, int stack,
|
|
float ang, float angspace, float spd1, float spd2, float scale,
|
|
int type){
|
|
|
|
int objPattern = ObjPatternShot_Create();
|
|
|
|
ObjPatternShot_SetParentObject(objPattern, parent);
|
|
|
|
if(ObjEnemyBossScene_GetInfo(GetEnemyBossSceneObjectID(), INFO_CURRENT_LIFE) == 0){Obj_Delete(objPattern); return;}
|
|
|
|
ObjPatternShot_SetPatternType(objPattern, PATTERN_LINE); // PATTERN_FAN or PATTERN_FAN_AIMED
|
|
ObjPatternShot_SetShotType(objPattern, OBJ_SHOT);
|
|
ObjPatternShot_SetInitialBlendMode(objPattern, BLEND_ALPHA);
|
|
|
|
ObjPatternShot_SetShotCount(objPattern, way, stack);
|
|
ObjPatternShot_SetSpeed(objPattern, spd1, spd2);
|
|
ObjPatternShot_SetAngle(objPattern, ang, angspace);
|
|
|
|
ObjPatternShot_SetBasePointOffset(objPattern, 0, 0);
|
|
//ObjPatternShot_SetDelay(objPattern, 15);
|
|
ObjPatternShot_SetGraphic(objPattern, graphic);
|
|
|
|
Shoot2;
|
|
int[] arrayPattern = ObjPatternShot_FireReturn(objPattern);
|
|
|
|
for each (int bullet in ref arrayPattern){
|
|
_BulletRescale(bullet, scale, true, 1);
|
|
_Delay(bullet, 10);
|
|
_EnemyShotFade(bullet, 600, true, 2.5); // Placeholder
|
|
Obj_SetRenderPriorityI(bullet, 51);
|
|
//if (reflect) {_ReflectToPlayer(bullet);}
|
|
}
|
|
|
|
async{
|
|
wait(600);
|
|
Obj_Delete(objPattern);
|
|
return;
|
|
}
|
|
|
|
return objPattern;
|
|
|
|
}
|
|
|
|
task endingnew(){
|
|
|
|
while(ObjEnemy_GetInfo(bossObj, INFO_LIFE)>0){
|
|
yield;
|
|
}
|
|
|
|
_GoToBrazil(objScene, bossObj, 12, 24);
|
|
wait(120);
|
|
ObjSound_SetVolumeRate(fire2, 20);
|
|
|
|
CloseScript(GetOwnScriptID);
|
|
|
|
}
|
|
|
|
|