Compare commits

..

2 Commits

Author SHA1 Message Date
kevinmonitor fb99733309 Small note added to README.md 2022-08-20 07:49:13 +00:00
kevinmonitor e171607abe Update attack progress #1 2022-08-20 05:48:22 +00:00
79 changed files with 2882 additions and 540 deletions

View File

@ -1,5 +1,11 @@
# ScarletBlackMarket
CURRENT FINISHED ATTACKS
+ Remilia Nonspell 1
+ Remilia Nonspell 2
+ Remilia Spell 3 (Sakuya Card)
Repository for Kevinmonitor and co.'s Touhou Station Jam 3 game.
How to install & play available attacks:
@ -17,6 +23,7 @@ How to change the difficulty of the attacks:
+ Open the .dnh file in a text editor (preferably Notepad++ since that's also what I use to code)
+ Search for the @Initialize section. (should be around lines 50-70 depending on attack)
+ Find the "difficultySelect = 0; // debug" line.
+ IF THIS LINE HAS A // BEFORE IT (example: "//difficultySelect = 0; // debug"), remove that //.
+ Change 0 to the difficulty you want to play:
0: Normal

Binary file not shown.

Before

Width:  |  Height:  |  Size: 896 KiB

After

Width:  |  Height:  |  Size: 844 KiB

View File

@ -40,27 +40,26 @@ task _RenderBoss(int enemy){
int aniidle = 0;
string tex = "script/game/resourceLib/Spritesheet_StationJam.png";
string tex = "script/game/StageLib/BossAsset.png";
LoadTextureEx(tex, true, true);
ObjPrim_SetTexture(enemy, tex);
ObjSprite2D_SetSourceRect(enemy, 1280, 0, 1280+256, 256);
ObjSprite2D_SetSourceRect(enemy, 0, 0, 512, 512);
ObjSprite2D_SetDestCenter(enemy);
ObjRender_SetScaleXYZ(enemy, 1);
ObjRender_SetScaleXYZ(enemy, 0.48);
int nameText = CreateTextObject(
GetStgFrameWidth()*0.3, 200, 25,
"REMILIA SCARLET", "Origami Mommy",
0xFF5A5A, 0xFFFFFF,
0x791D1D, 3,
GetStgFrameWidth()*0.38, 10, 42,
"BOSS: Chimata Tenkyuu", "Connecting Chain Handserif",
0x8ABFFF, 0xFFFFFF,
0x552A9C, 3,
1
);
ObjRender_SetAngleZ(nameText, -90);
ObjText_SetFontBold(nameText, true);
//_BossMarker(enemy, tex, 512, 0, 768, 256, 0.45, 30);
_BossMarker(enemy, tex, 512, 0, 768, 256, 0.45, 30);
while(!Obj_IsDeleted(enemy)){
@ -159,37 +158,6 @@ function <int> _CreateEnemy(
}
// Special overload for creating hitbox/hurtboxless enemies.
function <int> _CreateEnemy(
bool intersectionrender,
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);
if (intersectionrender){_HandleEnemyWellbeing(enemyID, sizeHitbox, sizeHurtbox);}
else{}
return enemyID;
}
// Hitbox and deletion
task _HandleEnemyWellbeing(int enemyID, float sizeHitbox, float sizeHurtbox){

View File

@ -39,7 +39,7 @@ let bossdiesnd = ObjSound_Create();
*/
int[] denseTrailSpiral = [7, 8, 10];
float[] wvelTrailBubble = [2.5, 3.5, 4.5];
float[] wvelTrailBubble = [3, 4, 5];
float[] spdTrailBubble = [13, 14, 16];
int[] denseTrailCurve = [1, 1, 1];
@ -67,12 +67,12 @@ See trail burst above but x2.
*/
int[] denseAimedSpiral = [9, 10, 11];
float[] wvelAimedBubble = [1.15, 1.35, 1.7];
float[] wvelAimedBubble = [1.25, 1.4, 1.75];
float[] spdAimedBubble = [12, 14, 16];
int[] delayAimed = [12, 10, 8];
float[] accelAimed = [8/60, 9/50, 10/40];
float[] maxspdAimed = [8, 9, 10];
int[] delayAimed = [11, 9, 7];
float[] accelAimed = [6/60, 8/50, 10/40];
float[] maxspdAimed = [7, 8, 9];
int[] delayBeforeAim = [40, 30, 20];
int[] delayFinalBurst = [45, 35, 30];
@ -90,7 +90,7 @@ int[] thresholdStopSpin = [80, 70, 60];
_InitDifficulty(difficultySelect);
difficultySelect = 1; // debug
//difficultySelect = 1; // debug
SetShotAutoDeleteClip(64, 64, 64, 64);
@ -330,22 +330,13 @@ task _ShotAimed(shot, ang){
float x2 = ObjMove_GetX(shot);
float y2 = ObjMove_GetY(shot);
let bullet = CreateShotA1(x2, y2, 0, ObjMove_GetAngle(shot), KEV_KNIFE_LAVENDER, 15);
let bullet = CreateShotA1(x2, y2, 0, ObjMove_GetAngle(shot), KEV_BUTTERFLY_LAVENDER, 15);
if(Obj_IsDeleted(shot)){Obj_Delete(bullet);}
Obj_SetRenderPriorityI(bullet, Obj_GetRenderPriorityI(shot)+1);
Pattern(bullet, del);
task Pattern(bullet, del){
float ang = ObjMove_GetAngle(bullet);
ascent(i in 0..10){
ObjMove_AddPatternA2(bullet, delayBeforeAim[difficultySelect]-del+i, NO_CHANGE, Interpolate_Decelerate(ang, GetAngleToPlayer(bullet), i/10), accelAimed[difficultySelect], maxspdAimed[difficultySelect], 0);
yield;
}
}
_BulletRescale(bullet, 1.25, true, 1);
ObjMove_AddPatternA4(bullet, delayBeforeAim[difficultySelect]-del, NO_CHANGE, 0, accelAimed[difficultySelect], maxspdAimed[difficultySelect], 0, NO_CHANGE, GetPlayerObjectID());
_BulletRescale(bullet, 0.85, true, 1);
_Delay(bullet, 15);
wait(delayAimed[difficultySelect]);

View File

@ -1,193 +0,0 @@
#TouhouDanmakufu[Single]
#ScriptVersion[3]
#Title["Remilia Spell 1 (Remilia 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:
- Remilia wait time
- Spiral density
- Spiral angle offset per frame
- Ring density
- Ring (max) speed
- Delays between rings
*/
int denseSpiral = [12, 15, 18];
int angleoffsetSpiral = [3, 4, 6];
int[] denseRing = [10, 12, 14];
float[] maxspeedRing = [8, 9, 11];
int[] deceltimeRing = [20, 30, 40];
int[] acceltimeRing = [60, 45, 35];
// Remilia takes 36 frames. Preferably, pick a number 36 can divide with.
int[] delayRing = [9, 6, 5];
// How much time Remilia takes to charge her attack and you get to GTFO.
int[] waittimeRemi = [75, 60, 50];
string tex = "script/game/resourceLib/Spritesheet_StationJam.png";
//LoadTextureEx(tex, true, true);
// Includes ahoy
#include "script/KevinSystem/Universal_Lib.txt" // The library to include all libraries :sans: :nail_care:
@Initialize {
//SetIntersectionVisualization(true);
SetAutoDeleteObject(true);
_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(4000);
}
case(EV_REQUEST_TIMER) {
SetScriptResult(40);
}
}
@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), 100);
yield;
}
@Finalize {
}
/*
Remi Spell 1
Remi moves to your current position (takes 36 frames), fires a dense star of stars (lol) that fades away quickly, and waits a set number of frames before repeating. After 3 waves of this, she moves to the middle and shoots a thick spiral of fireballs that requires you to spin around the screen for a bit.
She also leaves behind trails of rings with every dash.
Remilia takes 36 frames to move to you each time. -> delay between rings: 9, 6, 5
Parameters:
- Remilia wait time
- Spiral density
- Spiral speed
- Spiral angle offset per frame
- Ring density
- Ring speed
- Delays between rings
*/
task mainTask {
}
function <void> _VampireChase(){
ObjMove_SetDestAtFrame(bossObj, playerX, playerY, 36, LERP_DECELERATE);
loop(36/delayRing){
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.65, true, 1);
//Shoot1;
}
}
wait(delayRing);
}
}
task endingnew(){
while(ObjEnemy_GetInfo(bossObj, INFO_LIFE)>0){
yield;
}
_GoToBrazil(objScene, bossObj, 12, 24);
wait(120);
ObjSound_SetVolumeRate(fire2, 20);
CloseScript(GetOwnScriptID);
}

View File

@ -23,46 +23,31 @@ let spellsnd = ObjSound_Create();
let bossdiesnd = ObjSound_Create();
/*
Parameters:
- Line density
- Line speed
- Line delay
- Movement speed (in frames)
- Ring density
- Ring max speed
- Delays between rings
- Remilia charge time
- Ring speed
- Aim speed
- Density of one knife line
- Speed of knives
- Number of knife rings
- Density of each knife ring
- Speed of each knife ring
*/
int[] denseLine = [3, 4, 5];
int[] speedLine = [8.5, 10, 12];
float[] delayLine = [23, 20, 18];
int[] frameMove = [9*27, 9*25, 9*23]; // Divisible by 9
int[] denseRing = [15, 18, 21];
int[] speedRing = [4, 5, 6];
int[] denseRing = [10, 12, 14];
float[] maxspeedRing = [8, 9, 11];
int[] deceltimeRing = [20, 30, 40];
int[] acceltimeRing = [60, 45, 35];
int[] denseKnifeLine = [5, 6, 7];
float[] speedKnife = [8, 9, 10];
float[] spaceKnifeFan = [5, 7, 9];
// 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";
//LoadTextureEx(tex, true, true);
int[] ringcountKnife = [3, 5, 7];
int[] ringdenseKnife = [10, 12, 14];
float[] ringspeedKnife = [8, 9, 10];
// Includes ahoy
@ -93,7 +78,23 @@ string tex = "script/game/resourceLib/Spritesheet_StationJam.png";
ObjMove_SetPosition(bossObj, GetCommonData("Boss Position X", STG_WIDTH/2), GetCommonData("Boss Position Y", STG_HEIGHT/2));
ObjEnemy_SetMaximumDamage(bossObj, 999);
_RenderBoss(bossObj);
// PLACEHOLDER!
let imgExRumia = GetModuleDirectory() ~ "script/ExRumia/ExRumia.png";
ObjPrim_SetTexture(bossObj, imgExRumia);
ObjSprite2D_SetSourceRect(bossObj, 64, 1, 127, 64);
ObjSprite2D_SetDestCenter(bossObj);
ObjRender_SetScaleXYZ(bossObj, 2, 2, 1);
ObjRender_SetColor(bossObj, 0xB93C3C);
ObjMove_SetDestAtFrame(bossObj, STG_WIDTH/2, 550, 1);
// PLACEHOLDER!
/*_SoloCutin(
"script/invalid.png",
0, 0, 1, 1,
bossObj, objScene, "\"Euphoric Blooming of a New Market\"",
40, 4, 0x2868B9, 0x1D115D,
10, STG_HEIGHT*1/10-30, 41
);*/
//WriteLog(spellPIV);
WriteLog(ObjEnemyBossScene_GetInfo(objScene, INFO_SPELL_SCORE));
@ -108,23 +109,13 @@ string tex = "script/game/resourceLib/Spritesheet_StationJam.png";
alternative(GetEventType())
case(EV_REQUEST_LIFE) {
SetScriptResult(2500);
SetScriptResult(5200);
}
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 {
@ -164,68 +155,19 @@ Parameters:
- Ring density
- Ring speed
- Delays between rings
- Remilia charge time
- Remilia charge delay
- Aim speed
*/
task mainTask {
wait(45);
// 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.
}
/*
@ -236,193 +178,11 @@ 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.65, 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
task _CreateBook(
int[] [rectLeft, rectTop, rectRight, rectBottom],
float scale
){
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(){

View File

@ -38,12 +38,12 @@ Parameters:
*/
int[] frameMove = [9*27, 9*25, 9*23]; // Divisible by 9
int[] denseRing = [14, 16, 20];
int[] denseRing = [15, 18, 21];
int[] speedRing = [4, 5, 6];
int[] denseKnifeLine = [4, 5, 6];
float[] speedKnife = [8, 10, 12];
float[] spaceKnifeFan = [4, 6, 8];
int[] denseKnifeLine = [5, 6, 7];
float[] speedKnife = [8, 9, 10];
float[] spaceKnifeFan = [5, 7, 9];
int[] ringcountKnife = [3, 5, 7];
int[] ringdenseKnife = [10, 12, 14];
@ -78,7 +78,23 @@ float[] ringspeedKnife = [8, 9, 10];
ObjMove_SetPosition(bossObj, GetCommonData("Boss Position X", STG_WIDTH/2), GetCommonData("Boss Position Y", STG_HEIGHT/2));
ObjEnemy_SetMaximumDamage(bossObj, 999);
_RenderBoss(bossObj);
// PLACEHOLDER!
let imgExRumia = GetModuleDirectory() ~ "script/ExRumia/ExRumia.png";
ObjPrim_SetTexture(bossObj, imgExRumia);
ObjSprite2D_SetSourceRect(bossObj, 64, 1, 127, 64);
ObjSprite2D_SetDestCenter(bossObj);
ObjRender_SetScaleXYZ(bossObj, 2, 2, 1);
ObjRender_SetColor(bossObj, 0xB93C3C);
ObjMove_SetDestAtFrame(bossObj, STG_WIDTH/2, 550, 1);
// PLACEHOLDER!
/*_SoloCutin(
"script/invalid.png",
0, 0, 1, 1,
bossObj, objScene, "\"Euphoric Blooming of a New Market\"",
40, 4, 0x2868B9, 0x1D115D,
10, STG_HEIGHT*1/10-30, 41
);*/
//WriteLog(spellPIV);
WriteLog(ObjEnemyBossScene_GetInfo(objScene, INFO_SPELL_SCORE));
@ -237,20 +253,20 @@ function <void> MoveToBottom(){
// If you're at bottom, you die. (Hyper and above)
if(difficultySelect >= 0){
loop([6, 7, 9][difficultySelect]){
loop(9){
float ang = GetAngleToPlayer(bossObj);
float[] spawn = [bossX+rand(-25, 25), bossY+rand(-25, 25)];
ascent(i in -1..denseRing[difficultySelect]-1){
int shot = CreateShotA2(spawn[0], spawn[1], speedRing[difficultySelect]*2.5, ang+(360/denseRing[difficultySelect])*i, -speedRing[difficultySelect]*2.5/[30, 40, 45][difficultySelect], speedRing[difficultySelect]*[0.3, 0.2, 0.1][difficultySelect], KEV_KNIFE_LAVENDER, 10);
int shot = CreateShotA2(spawn[0], spawn[1], speedRing[difficultySelect]*2.5, ang+(360/denseRing[difficultySelect])*i, -speedRing[difficultySelect]*2.5/[30, 40, 45][difficultySelect], speedRing[difficultySelect]*[0.3, 0.2, 0.1][difficultySelect], KEV_KNIFE_RED, 10);
if(ObjEnemy_GetInfo(bossObj, INFO_LIFE) == 0){Obj_Delete(shot);}
else{
ObjMove_AddPatternA2(shot, 90, NO_CHANGE, NO_CHANGE, speedRing[difficultySelect]/45, speedRing[difficultySelect]*[1.65, 1.4, 1.25][difficultySelect], 0);
_Delay(shot, 10);
//ObjRender_SetBlendType(shot, BLEND_ADD_ARGB);
ObjRender_SetBlendType(shot, BLEND_ADD_ARGB);
_BulletRescale(shot, 1.25, true, 1);
Shoot2;
Shoot1;
if(difficultySelect >= 1){_ReflectToPlayer(shot);}
}
}
@ -265,11 +281,11 @@ function <void> MoveToBottom(){
loop(3){
CreateFan(3, KEV_KNIFE_LAVENDER, denseKnifeLine[difficultySelect],
0, spaceKnifeFan[difficultySelect], speedKnife[difficultySelect]/1.18, speedKnife[difficultySelect], 1.25,
0, spaceKnifeFan[difficultySelect], speedKnife[difficultySelect]/1.3, speedKnife[difficultySelect], 1.25,
PATTERN_FAN, true);
CreateFan(3, KEV_KNIFE_LAVENDER, denseKnifeLine[difficultySelect],
180, spaceKnifeFan[difficultySelect], speedKnife[difficultySelect]/1.18, speedKnife[difficultySelect], 1.25,
180, spaceKnifeFan[difficultySelect], speedKnife[difficultySelect]/1.3, speedKnife[difficultySelect], 1.25,
PATTERN_FAN, true);
wait(frameMove[difficultySelect]/3);
@ -329,7 +345,7 @@ task _ReflectToPlayer(int target){
if(ObjEnemy_GetInfo(bossObj, INFO_LIFE) == 0){Obj_Delete(target); return;}
if (ObjMove_GetX(target) <= -15 || ObjMove_GetX(target) >= GetStgFrameWidth()+15){
ObjMove_AddPatternA4(target, 0, NO_CHANGE, 0, NO_CHANGE, NO_CHANGE, 0, KEV_KNIFE_GREEN, GetPlayerObjectID());
Shoot1;
Shoot2;
ObjShot_SetAutoDelete(target, true);
break;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 KiB

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -0,0 +1,452 @@
#TouhouDanmakufu[Player]
#ScriptVersion[3]
#ID["Rinno"]
#Title["Rinnosuke"]
#Text["Unfocused Shot: Rage of the Electronic[r]Focused Shot: Doomsday Button[r]Spell Card: Prototype \"Volatile Hakkero\""]
//#Image["./kevkou_lib/gayimg.png"]
#ReplayName["Rinno"]
#include "script/KevinSystem/Kevin_PlayerLib.txt"
#include "./Rinnosuke_ShotConst.dnh"
#include "./soundlib.txt"
#include "script/KevinSystem/kevin_system/Kevin_ItemConst.txt"
#include "script/KevinSystem/kevin_system/Kevin_ItemLib.txt"
let csd = GetCurrentScriptDirectory();
// Global Variables
float maxX = GetStgFrameWidth();
float maxY = GetStgFrameHeight();
int targetedenemy = 0; // This will be used to store the enemy ID Kouda's homing shots aim at
// Images & Sound
let teamimg = "script/TouhouJam8_SRE/Jam8_Resource/jam8Sprite.png";
LoadTextureEx(teamimg, true, true);
//ObjRender_SetTextureFilterMip(teamimg, FILTER_LINEAR);
let sndpath = csd ~ "./sound";
// Other stuff
float playerX = 0;
float playerY = 0;
let objPlayer = GetPlayerObjectID();
int plrender = Obj_GetRenderPriorityI(objPlayer);
bool ripplayer = false;
float shotspeed = 0;
float bombrand = 0;
bool bombenable = false;
bool focusactive = false;
bool ishoming = false;
int[] _enemyArray = []; // Prepare an array to store enemy IDs for Kouda's homing shot
int[] _existArray = [];
int grazecounter = 0; // For basic graze = PIV mechanic
// Custom events for scoring mechanic
const EV_PIV_100 = EV_USER + 100i; // Normal enemies and nons
const EV_PIV_250 = EV_USER + 101i; // Spells
const EV_PIV_500 = EV_USER + 102i; // Last Spells
const EV_PIV_2000 = EV_USER + 103i; // What.
@Initialize{
if(!IsCommonDataAreaExists("PIV")){
CreateCommonDataArea("PIV");
SetAreaCommonData("PIV", "currentvalue", 10000);
}
else{}
// Stuff
parameterrender();
playerrender();
plrender = Obj_GetRenderPriorityI(objPlayer);
_SoundTask();
_Mechanic(ripplayer, _enemyArray, _existArray, GetStgFrameWidth(), GetStgFrameHeight(), objPlayer, GetEnemyBossSceneObjectID(), 3, 2, 30);
_HitboxRender(ripplayer, objPlayer, teamimg, teamimg, 512, 0, 640, 128, 768, 0, 1024, 256, 0.2, 1);
// Shot functions
//_EnemySelect();
_Homing();
//_UnfocusedShot();
//_ShotType();
// Shot data loading
LoadPlayerShotData(csd ~ "./Rinnosuke_ShotData.dnh");
}
@MainLoop{
_enemyArray = GetIntersectionRegistedEnemyID; // Constantly update the enemy ID array
shotspeed += 1; // Managing the shot rate
playerX = ObjMove_GetX(objPlayer);
playerY = ObjMove_GetY(objPlayer);
yield;
}
@Event{
alternative(GetEventType)
// PIV-item spawning events
case(EV_DELETE_SHOT_PLAYER){
let graphic = GetEventArgument(2);
float[] position = GetEventArgument(1);
let obj = CreatePlayerShotA1(position[0], position[1], 0, 0, 0, 99999, graphic);
ObjShot_SetIntersectionEnable(obj, false);
_DeleteEffect(obj);
}
case(EV_PIV_100){
let arg = GetEventArgument(0);
CreatePIVItem(PIV_100, arg[0], arg[1]);
}
case(EV_PIV_250){
let arg = GetEventArgument(0);
CreatePIVItem(PIV_250, arg[0], arg[1]);
}
case(EV_PIV_500){
let arg = GetEventArgument(0);
CreatePIVItem(PIV_500, arg[0], arg[1]);
}
// Basic functionality events
case(EV_REQUEST_SPELL){
let bomb = GetPlayerSpell();
if (bomb >= 1){
SetScriptResult(true);
SetPlayerSpell(bomb - 1);
Bomb();
_SigilCall(false, teamimg, 256, 512, 512, 768, objPlayer, GetPlayerInvincibilityFrame());
}
else {
SetScriptResult(false);
}
}
case(EV_HIT){
//_DeathbombWarning(teamimg, [2560, 512, 2560+512, 1024], 30, 0.65);
ObjSound_Play(predeathsfx);
}
case(EV_PLAYER_SHOOTDOWN){
ObjSound_Play(deathsfx);
ripplayer = true;
_SigilCall(true, teamimg, 0, 512, 256, 768, objPlayer, 120);
}
case(EV_PLAYER_REBIRTH){
ripplayer = false;
SetPlayerInvincibilityFrame(180);
_SigilCall(false, teamimg, 256, 512, 512, 768, objPlayer, GetPlayerInvincibilityFrame());
SetPlayerSpell( max(3, GetPlayerSpell()) );
}
case(EV_GRAZE){
grazecounter += GetEventArgument(0);
while(grazecounter >= 10){
SetAreaCommonData("PIV", "currentvalue", GetAreaCommonData("PIV", "currentvalue")+10);
grazecounter -= 10;
}
}
}
@Finalize{
}
task _DeleteEffect(obj){
ObjRender_SetBlendType(obj, BLEND_ADD_ARGB);
ascent(i in 0..30){
ObjRender_SetAlpha(obj, Interpolate_Decelerate(120, 0, i/30));
ObjRender_SetScaleXYZ(obj, Interpolate_Decelerate(0.5, 2, i/30));
yield;
}
Obj_Delete(obj);
}
// Basic player parameters
task parameterrender(){
SetPlayerItemScope(200); // Special Ability 1
SetPlayerLife(5); // Debug
SetPlayerSpell(3);
SetPlayerSpeed(6.5, 2.8); // (original: 4.5/1.75)
SetPlayerRebirthFrame(30); // Special Ability 2
SetPlayerAutoItemCollectLine(GetStgFrameHeight/3); //596x632 STG frame
SetPlayerRebirthLossFrame(0);
ObjPlayer_AddIntersectionCircleA1(objPlayer,0,0,1.5,25);
}
// Renders the shottype
// Selects an enemy to home on.
task _HomeShot(int shot_) {
float duration = 55;
bool homingBool = false;
float basepenetrate = ObjShot_GetPenetration(shot_);
float basedmg = ObjShot_GetDamage(shot_);
// Original code
async{
_BulletRescalePlayer(shot_, 0.75, true, 1.1);
}
//
for (int t = 0i; t < duration && !Obj_IsDeleted(shot_); t++) {
// Checks if enemies are on screen. If enemies are visible, enable homing for the shots.
// _enemyArray is an array containing all enemy IDs, and is constantly updated in @MainLoop.
if (0 < length(_enemyArray)) {
float targetDist = 2000; // Arbitrary number (???)
int targetID = 0;
// Checks distance of every enemy on screen.
for each (int enemy in ref _enemyArray) {
float enemyX = ObjMove_GetX(enemy);
float enemyY = ObjMove_GetY(enemy);
if (0 < enemyX && enemyX < maxX && 0 < enemyY && enemyY < maxY) {
// Returns the hypotenuse of the triangle formed by the x & y distances between the shot and the enemy.
float shotDist = hypot(enemyX - ObjMove_GetX(objPlayer), enemyY - ObjMove_GetY(objPlayer));
// Locks the shot onto the enemy.
if (shotDist < targetDist) {
targetDist = shotDist;
targetID = enemy;
homingBool = true;
}
}
}
// Code to handle the actual homing.
if (homingBool) {
for (int f = 0; t < duration && !Obj_IsDeleted(shot_) && !Obj_IsDeleted(targetID) && ObjEnemy_GetInfo(targetID, INFO_LIFE) != 0 && !ObjCol_IsIntersected(shot_); t++) {
ObjShot_SetAutoDelete(shot_, false);
float shotAngle = NormalizeAngle(ObjMove_GetAngle(shot_)); // Angle of the player shot
float targetAngle = NormalizeAngle(atan2(ObjMove_GetY(targetID) - ObjMove_GetY(shot_), ObjMove_GetX(targetID) - ObjMove_GetX(shot_))); // Returns angle from the shot to the enemy.
float angleDistance = AngularDistance(shotAngle, targetAngle); // Angular distance between enemy and player shot
float homeRate = Interpolate_Decelerate(0, 0.75, min(60, f) / 60);
// Homing speed?
ObjMove_SetAngle(shot_, Interpolate_Accelerate(shotAngle, shotAngle + angleDistance, homeRate)); // Interpolate_Necko
f++;
yield;
}
ObjShot_SetAutoDelete(shot_, true);
ObjMove_SetAngularVelocity(shot_, 0);
homingBool = false;
}
}
yield;
}
}
task _Homing(){
let optionA = PlayerOption(50, 0, 256, 256, 512, 512);
let optionB = PlayerOption(-50, 0, 256, 256, 512, 512);
let optionC = PlayerOption(90, 30, 256, 256, 512, 512);
let optionD = PlayerOption(-90, 30, 256, 256, 512, 512);
int shotspeedhome = 0;
int[] optionList = [optionA, optionB, optionC, optionD];
int[] optionList1 = [optionA, optionC];
int[] optionList2 = [optionB, optionD];
int a = 0;
loop{
if(shotspeedhome % 5 == 0 && GetVirtualKeyState(VK_SHOT) != KEY_FREE && IsPermitPlayerShot && !ripplayer){
if(GetVirtualKeyState(VK_SLOWMOVE) != KEY_FREE){
for each (int option in ref optionList){
//"l
float x = ObjRender_GetX(option);
float y = ObjRender_GetY(option);
let shotA = CreatePlayerShotA1(x, y, 15, 270, 2.65, 2, FOCUSED);
//ObjMove_AddPatternA2(shotA, 1, 5, NO_CHANGE, -0.1, 3.75, rand(-0.4, 0.4));
_HomeShot(shotA);
}
ascent(i in -2..3){
int shot = CreatePlayerShotA1(playerX, playerY, 17, 270-5*i, 3.5, 2, UNFOCUSED);
_BulletRescalePlayer(shot, 0.5, true, 1);
}
}
else{
for each (int option in ref optionList1){
//"
float x = ObjRender_GetX(option);
float y = ObjRender_GetY(option);
ascent(i in -1..2){
int shot = CreatePlayerShotA1(x, y, 17, 240-10*i, 4, 1.75, UNFOCUSED);
_BulletRescalePlayer(shot, 0.5, true, 1);
}
}
for each (int option in ref optionList2){
//"
float x = ObjRender_GetX(option);
float y = ObjRender_GetY(option);
ascent(i in -1..2){
int shot = CreatePlayerShotA1(x, y, 17, 300+10*i, 4, 1.75, UNFOCUSED);
_BulletRescalePlayer(shot, 0.5, true, 1);
}
}
}
}
shotspeedhome++;
yield;
}
}
// Player option rendering
function PlayerOption(offsetx, offsety, left, top, right, bottom){
let option = ObjPrim_Create(OBJ_SPRITE_2D);
bool visible;
int animate = 0;
float optx;
float opty;
ObjPrim_SetTexture(option, teamimg);
ObjSprite2D_SetSourceRect(option, left, top, right, bottom);
ObjSprite2D_SetDestCenter(option);
ObjRender_SetScaleXYZ(option, 0.25, 0.25, 1);
ObjRender_SetBlendType(option, BLEND_ALPHA);
ObjRender_SetAlpha(option, 180);
Obj_SetRenderPriorityI(option, 41);
ObjRender_SetPosition(option, offsetx, offsety, 1);
async{
loop{
ObjRender_SetPosition(option, GetPlayerX()+offsetx, GetPlayerY()+offsety, 1);
yield;
}
}
async{
loop{
if(ripplayer){Obj_SetVisible(option, false); visible = false;}
else {Obj_SetVisible(option, true); visible = true;}
yield;
}
}
return option;
}
// Player sprites
task playerrender(){
// Why is this movement code so cursed jesus fucking christ
float scale = 0.4; // Scalies
ObjPrim_SetTexture(objPlayer, teamimg);
ObjSprite2D_SetSourceRect(objPlayer, 0, 256, 256, 512);
ObjSprite2D_SetDestCenter(objPlayer);
Obj_SetRenderPriorityI(objPlayer, 42);
ObjRender_SetScaleXYZ(objPlayer, scale, scale, 1);
}
task Bomb(){
// Preparation
//SetForbidPlayerShot(true);
SetForbidPlayerSpell(true);
SetPlayerInvincibilityFrame(300);
// Spell object
let manageObj = GetSpellManageObject(); // SPELL BEGINS
ObjSpell_Regist(manageObj);
ObjSound_Play(bombsfx);
// Spell
int hakkero = CreatePlayerShotA1(playerX, playerY, 0, 0, 12, 999999, BOMB);
ObjShot_SetIntersectionEnable(hakkero, true);
ObjShot_SetEraseShot(hakkero, true);
ObjShot_SetSpellFactor(hakkero, true);
async{
ascent(i in 0..45){
_BulletRescalePlayer(hakkero, Interpolate_Decelerate(0.5, 2.25, i/45), true, 1);
ObjRender_SetAlpha(hakkero, Interpolate_Decelerate(0, 255, i/45));
yield;
}
}
ascent(i in 0..240){
yield;
}
// Cleanup, end of spell
ObjShot_FadeDelete(hakkero);
SetForbidPlayerShot(false);
wait(60);
SetForbidPlayerSpell(false);
Obj_Delete(manageObj); // !!! IMPORTANT !!!
}
// Screenshake function for bomb's duration - adapted from Sparen's tutorials
task _BombShake(shaketime, intensity){
float baseintensity = intensity;
float shakeno = shaketime;
ascent(i in 0..shakeno){
Set2DCameraFocusX(GetStgFrameWidth/2 + rand(-intensity, intensity));
Set2DCameraFocusY(GetStgFrameHeight/2 + rand(-intensity, intensity));
intensity = Interpolate_Decelerate(0, baseintensity, 1-i/shakeno);
shaketime--;
yield;
}
while(shaketime > 0){yield;}
Set2DCameraFocusX(GetStgFrameWidth/2);
Set2DCameraFocusY(GetStgFrameHeight/2);
yield;
}

View File

@ -0,0 +1,7 @@
let current = GetCurrentScriptDirectory();
let path = current ~ "Rinnosuke_ShotData.dnh";
LoadPlayerShotData(path);
// -----
const FOCUSED = 1;
const UNFOCUSED = 2;
const BOMB = 3;

View File

@ -0,0 +1,14 @@
shot_image = "./../../TouhouJam8_SRE/Jam8_Resource/jam8Sprite.png"
ShotData{
id = 0 // Dummy
rect = (0,0,0,0)
render = ALPHA
alpha = 0
collision = 12
}
// Rinnosuke's shots
ShotData{id = 1 rect = (512, 256, 639, 383) render = ALPHA alpha = 180 collision = 38 fixed_angle = false angular_velocity = 3} // Homing shot, spins
ShotData{id = 2 rect = (640, 256, 768, 384) render = ALPHA alpha = 150 collision = 38} // Unfocused shot
ShotData{id = 3 rect = (768, 256, 1023, 511) render = ADD_ARGB alpha = 255 collision = 115 fixed_angle = false angular_velocity = 8} // Bomb

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,25 @@
let sddir = GetCurrentScriptDirectory() ~ "./sound";
function LoadEx(targetobj, targetpath, targetvol){
ObjSound_Load(targetobj, targetpath);
ObjSound_SetVolumeRate(targetobj, targetvol);
}
// Universal sounds
let bomb = sddir ~ "./bfxr_RinnoBomb.wav";
let ded = sddir ~ "./bfxr_PlayerDie.wav";
let hit = sddir ~ "./bfxr_PreDeath.wav";
let bombsfx = ObjSound_Create();
let deathsfx = ObjSound_Create();
let predeathsfx = ObjSound_Create();
task _SoundTask(){
LoadEx(bombsfx, bomb, 15);
LoadEx(deathsfx, ded, 40);
LoadEx(predeathsfx, hit, 55);
}

View File

@ -0,0 +1,218 @@
// Particle list for laser effect
int LaserEffect = ObjParticleList_Create(OBJ_PARTICLE_LIST_2D);
int[] rect = [2816, 0, 2816+256, 0+256];
ObjPrim_SetTexture(LaserEffect, "script/player/PrideJam_ByakMiko/playerlib/ByakMiko_Sheet.png");
Obj_SetRenderPriorityI(LaserEffect, 41);
ObjPrim_SetPrimitiveType(LaserEffect, PRIMITIVE_TRIANGLELIST);
ObjPrim_SetVertexCount(LaserEffect, 4);
ObjRender_SetBlendType(LaserEffect, BLEND_ADD_ARGB);
// Left-top, right-top, left-bottom, right-bottom
ObjPrim_SetVertexUVT(LaserEffect, 0, rect[0], rect[1]);
ObjPrim_SetVertexUVT(LaserEffect, 1, rect[2], rect[1]);
ObjPrim_SetVertexUVT(LaserEffect, 2, rect[0], rect[3]);
ObjPrim_SetVertexUVT(LaserEffect, 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(LaserEffect, 0, -dU, -dV, 1);
ObjPrim_SetVertexPosition(LaserEffect, 1, dU, -dV, 1);
ObjPrim_SetVertexPosition(LaserEffect, 2, -dU, dV, 1);
ObjPrim_SetVertexPosition(LaserEffect, 3, dU, dV, 1);
ObjPrim_SetVertexIndex(LaserEffect, [0, 1, 2, 1, 2, 3]);
task _CreateLaserParticle(float x, float y, float spdX, float spdY, float baseAng){
int effectLength = 45;
let x_speed = spdX;
let y_speed = spdY;
let z_add = rand(-5, 5);
ascent(i in 0..effectLength){
_PetalMovement(Interpolate_Decelerate(0.5, 0.2, i/effectLength), Interpolate_Decelerate(255, 0, i/effectLength));
yield;
}
task _PetalMovement(scale, alpha){
ObjParticleList_SetScale(LaserEffect, scale);
ObjParticleList_SetAngleZ(LaserEffect, baseAng);
ObjParticleList_SetPosition(LaserEffect, x, y, 1);
ObjParticleList_SetAlpha(LaserEffect, alpha);
//Submits the current data to an instance, cleared every frame.
ObjParticleList_AddInstance(LaserEffect);
x += x_speed;
y += y_speed;
baseAng += z_add;
yield;
}
}
//
task _LaserSpriteRender(
img, int target, float targetAng,
int rectLeft, int rectTop, int rectRight, int rectBottom,
float scaleX, float scaleY, int renderPriority, float alpha,
float scaleSpeed
){
int[] EnemyList = [];
let lasersprite = ObjPrim_Create(OBJ_SPRITE_2D);
ObjPrim_SetTexture(lasersprite, img);
ObjSprite2D_SetSourceRect(lasersprite, rectLeft, rectTop, rectRight, rectBottom);
ObjRender_SetScaleXYZ(lasersprite, 0, scaleY, 1);
ObjSprite2D_SetDestRect(lasersprite, -(rectRight-rectLeft)/2, 0, (rectRight-rectLeft)/2, rectTop-rectBottom);
Obj_SetRenderPriorityI(lasersprite, renderPriority);
ObjRender_SetAlpha(lasersprite, 0);
ObjRender_SetBlendType(lasersprite, BLEND_ADD_ARGB);
async{
loop{
EnemyList = ObjCol_GetListOfIntersectedEnemyID(target);
if(GetVirtualKeyState(VK_SHOT) != KEY_FREE && GetVirtualKeyState(VK_SLOWMOVE) != KEY_FREE && !ripplayer && IsPermitPlayerShot){
// Assumes the intersecting color is right next to the right of the original color
if (length(EnemyList) > 0) {ObjSprite2D_SetSourceRect(lasersprite, rectRight, rectTop, rectRight+(rectRight-rectLeft), rectBottom);}
else{ObjSprite2D_SetSourceRect(lasersprite, rectLeft, rectTop, rectRight, rectBottom);}
float targetx = ObjRender_GetX(target);
float targety = ObjRender_GetY(target);
ObjRender_SetScaleX(lasersprite, min(scaleX+rand(-0.025, 0.025), ObjRender_GetScaleX(lasersprite) + scaleSpeed));
ObjRender_SetAlpha(lasersprite, min(alpha, ObjRender_GetAlpha(lasersprite)+alpha/5));
ObjRender_SetAngleZ(lasersprite, targetAng);
ObjRender_SetPosition(lasersprite, targetx, targety, 1);
yield;
}
else{
ObjRender_SetScaleX(lasersprite, max(0, ObjRender_GetScaleX(lasersprite) - scaleSpeed));
ObjRender_SetAlpha(lasersprite, max(0, ObjRender_GetAlpha(lasersprite)-alpha/5));
yield;
}
yield;
}
}
}
function _RenderLaser(target, float ang, float maxintersectX, float maxLength, float width, float speedLength, float dmg){ /* */
int[] EnemyList = [];
float angRender = asin(maxintersectX/absolute(maxLength));
//int laser = CreatePlayerShotA1(ObjRender_GetX(target), ObjRender_GetY(target), 0, ang, dmg, 9999999, 0);
int laser = CreateStraightLaserA1(ObjRender_GetX(target), ObjRender_GetY(target), ang-90, maxLength, width, 99999999, 0, 1);
ObjLaser_SetInvalidLength(laser, 0, 0);
ObjShot_SetDamage(laser, dmg);
ObjShot_SetAutoDelete(laser, false);
Obj_SetRenderPriorityI(laser, 38);
_Follow(laser, target);
_LaserSpriteRender(
teamimg, laser, ang,
3072, 0, 3072+256, 256,
0.45, 20, 38, 255*(universalAlpha/100),
0.14
);
// This also affects damage of the laser, not just effects
LaserHitEffect(EnemyList, laser, dmg);
// When shot key is being held, create a line intersection that stretches across the laser.
/*
Calculations:
startx: x of target
starty: y of target
endx: x of laser
endy: maxLength
width = width of laser
*/
async{
loop{
if(GetVirtualKeyState(VK_SHOT) != KEY_FREE && GetVirtualKeyState(VK_SLOWMOVE) != KEY_FREE && !ripplayer && IsPermitPlayerShot){
if(shotspeed % 5 == 0){
ObjSound_Play(inferno);
}
ObjShot_SetIntersectionEnable(laser, true);
//ObjShot_SetIntersectionLine(laser, ObjRender_GetX(target), ObjRender_GetY(target), ObjRender_GetX(target)+maxintersectX, -maxLength, width);
//WriteLog(ObjMove_GetX(laserfwd));
yield;
}
else{
ObjShot_SetIntersectionEnable(laser, false);
ObjShot_SetPenetration(laser, 99999999);
yield;
}
}
}
// After shot key is released, let the laser leave and then delete it.
return laser;
}
task LaserHitEffect(int[] EnemyList, int target, float basedmg){
async{
loop{
EnemyList = ObjCol_GetListOfIntersectedEnemyID(target);
yield;
}
}
// Particle effects
async{
loop{
ascent(i in -1..length(EnemyList)-1){
_CreateLaserParticle(ObjMove_GetX(EnemyList[i])+prand(-30, 30), ObjMove_GetY(EnemyList[i])+prand(-30, 30), [prand(-8, -5), prand(-4, 4), prand(5, 8)][prand_int(0, 2)], [prand(-8, -6), prand(6, 8)][prand_int(0, 1)], rand(0, 360));
}
//Resort;
//WriteLog(EnemyList);
wait(rand_int(9, 11));
}
}
// Damage effects
}
task _Follow(follower, followed){
while(!Obj_IsDeleted(follower)){
float x = ObjRender_GetX(followed);
float y = ObjRender_GetY(followed);
ObjMove_SetPosition(follower, x, y);
yield;
}
}

View File

@ -0,0 +1,246 @@
// Particle list for laser effect
int LaserEffect = ObjParticleList_Create(OBJ_PARTICLE_LIST_2D);
int[] rect = [2816, 0, 2816+256, 0+256];
ObjPrim_SetTexture(LaserEffect, "script/player/PrideJam_ByakMiko/playerlib/ByakMiko_Sheet.png");
Obj_SetRenderPriorityI(LaserEffect, 39);
ObjPrim_SetPrimitiveType(LaserEffect, PRIMITIVE_TRIANGLELIST);
ObjPrim_SetVertexCount(LaserEffect, 4);
ObjRender_SetBlendType(LaserEffect, BLEND_ALPHA);
// Left-top, right-top, left-bottom, right-bottom
ObjPrim_SetVertexUVT(LaserEffect, 0, rect[0], rect[1]);
ObjPrim_SetVertexUVT(LaserEffect, 1, rect[2], rect[1]);
ObjPrim_SetVertexUVT(LaserEffect, 2, rect[0], rect[3]);
ObjPrim_SetVertexUVT(LaserEffect, 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(LaserEffect, 0, -dU, -dV, 1);
ObjPrim_SetVertexPosition(LaserEffect, 1, dU, -dV, 1);
ObjPrim_SetVertexPosition(LaserEffect, 2, -dU, dV, 1);
ObjPrim_SetVertexPosition(LaserEffect, 3, dU, dV, 1);
ObjPrim_SetVertexIndex(LaserEffect, [0, 1, 2, 1, 2, 3]);
task _CreateLaserParticle(float x, float y, float spdX, float spdY, float baseAng){
int effectLength = 45;
let x_speed = spdX;
let y_speed = spdY;
let z_add = rand(-5, 5);
ascent(i in 0..effectLength){
_PetalMovement(Interpolate_Decelerate(0.5, 0.2, i/effectLength), Interpolate_Decelerate(255, 0, i/effectLength));
yield;
}
task _PetalMovement(scale, alpha){
ObjParticleList_SetScale(LaserEffect, scale);
ObjParticleList_SetAngleZ(LaserEffect, baseAng);
ObjParticleList_SetPosition(LaserEffect, x, y, 1);
ObjParticleList_SetAlpha(LaserEffect, alpha);
//Submits the current data to an instance, cleared every frame.
ObjParticleList_AddInstance(LaserEffect);
x += x_speed;
y += y_speed;
baseAng += z_add;
yield;
}
}
//
task _LaserSpriteRender(
img, int target, float targetAng,
int rectLeft, int rectTop, int rectRight, int rectBottom,
float scaleX, float scaleY, int renderPriority, float alpha,
float scaleSpeed
){
int[] EnemyList = [];
let lasersprite = ObjPrim_Create(OBJ_SPRITE_2D);
ObjPrim_SetTexture(lasersprite, img);
ObjSprite2D_SetSourceRect(lasersprite, rectLeft, rectTop, rectRight, rectBottom);
ObjRender_SetScaleXYZ(lasersprite, 0, scaleY, 1);
ObjSprite2D_SetDestRect(lasersprite, -(rectRight-rectLeft)/2, 0, (rectRight-rectLeft)/2, rectTop-rectBottom);
Obj_SetRenderPriorityI(lasersprite, renderPriority);
ObjRender_SetAlpha(lasersprite, 0);
ObjRender_SetBlendType(lasersprite, BLEND_ADD_ARGB);
async{
loop{
EnemyList = ObjCol_GetListOfIntersectedEnemyID(target);
if(GetVirtualKeyState(VK_SHOT) != KEY_FREE && GetVirtualKeyState(VK_SLOWMOVE) != KEY_FREE && !ripplayer && IsPermitPlayerShot){
// Assumes the intersecting color is right next to the right of the original color
if (length(EnemyList) > 0) {ObjSprite2D_SetSourceRect(lasersprite, rectRight, rectTop, rectRight+(rectRight-rectLeft), rectBottom);}
else{ObjSprite2D_SetSourceRect(lasersprite, rectLeft, rectTop, rectRight, rectBottom);}
float targetx = ObjRender_GetX(target);
float targety = ObjRender_GetY(target);
ObjRender_SetScaleX(lasersprite, min(scaleX+rand(-0.025, 0.025), ObjRender_GetScaleX(lasersprite) + scaleSpeed));
ObjRender_SetAlpha(lasersprite, min(alpha, ObjRender_GetAlpha(lasersprite)+alpha/5));
ObjRender_SetAngleZ(lasersprite, targetAng);
ObjRender_SetPosition(lasersprite, targetx, targety, 1);
yield;
}
else{
ObjRender_SetScaleX(lasersprite, max(0, ObjRender_GetScaleX(lasersprite) - scaleSpeed));
ObjRender_SetAlpha(lasersprite, max(0, ObjRender_GetAlpha(lasersprite)-alpha/5));
yield;
}
yield;
}
}
}
function _RenderLaser(target, float ang, float maxintersectX, float maxLength, float width, float speedLength, float dmg){ /* */
int[] EnemyList = [];
float angRender = asin(maxintersectX/absolute(maxLength));
//int laser = CreatePlayerShotA1(ObjRender_GetX(target), ObjRender_GetY(target), 0, ang, dmg, 9999999, 0);
int laser = CreateStraightLaserA1(ObjRender_GetX(target), ObjRender_GetY(target), ang-90, maxLength, width, 99999999, 0, 1);
ObjLaser_SetInvalidLength(laser, 0, 0);
//ObjShot_SetDamage(laser, dmg);
ObjShot_SetAutoDelete(laser, false);
Obj_SetRenderPriorityI(laser, 38);
_Follow(laser, target);
_LaserSpriteRender(
teamimg, laser, ang,
3072, 0, 3072+256, 256,
0.45, 20, 38, 200,
0.14
);
// This also affects damage of the laser, not just effects
LaserHitEffect(EnemyList, laser, dmg);
// When shot key is being held, create a line intersection that stretches across the laser.
/*
Calculations:
startx: x of target
starty: y of target
endx: x of laser
endy: maxLength
width = width of laser
*/
async{
loop{
if(GetVirtualKeyState(VK_SHOT) != KEY_FREE && GetVirtualKeyState(VK_SLOWMOVE) != KEY_FREE && !ripplayer && IsPermitPlayerShot){
if(shotspeed % 5 == 0){
ObjSound_Play(inferno);
}
ObjShot_SetIntersectionEnable(laser, true);
//ObjShot_SetIntersectionLine(laser, ObjRender_GetX(target), ObjRender_GetY(target), ObjRender_GetX(target)+maxintersectX, -maxLength, width);
//WriteLog(ObjMove_GetX(laserfwd));
yield;
}
else{
ObjShot_SetIntersectionEnable(laser, false);
ObjShot_SetPenetration(laser, 99999999);
yield;
}
}
}
// After shot key is released, let the laser leave and then delete it.
return laser;
}
task LaserHitEffect(int[] EnemyList, int target, float basedmg){
int[] ResortList = [];
// Debugging: gets distance from player to the laser, appends it into an array
task Resort(){
for each(int enm in EnemyList){
ResortList = append(ResortList, GetObjectDistance(enm, target));
}
WriteLog(ResortList);
ResortList = [];
}
async{
loop{
EnemyList = ObjCol_GetListOfIntersectedEnemyID(target);
yield;
}
}
async{
loop{
Resort();
wait(60);
}
}
// Particle effects
async{
loop{
ascent(i in -1..length(EnemyList)-1){
_CreateLaserParticle(ObjMove_GetX(EnemyList[i])+prand(-30, 30), ObjMove_GetY(EnemyList[i])+prand(-30, 30), [prand(-8, -5), prand(-4, 4), prand(5, 8)][prand_int(0, 2)], [prand(-8, -6), prand(6, 8)][prand_int(0, 1)], rand(0, 360));
}
//Resort;
//WriteLog(EnemyList);
wait(rand_int(9, 11));
}
}
// Damage effects
async{
loop{
ascent(i in -1..length(EnemyList)-1){
float dmgCalc = max(1, basedmg - (basedmg * 0.2 * i));
ObjEnemy_SetLife(EnemyList[i], ObjEnemy_GetInfo(EnemyList[i], INFO_LIFE) - dmgCalc);
}
yield;
}
}
}
task _Follow(follower, followed){
while(!Obj_IsDeleted(follower)){
float x = ObjRender_GetX(followed);
float y = ObjRender_GetY(followed);
ObjMove_SetPosition(follower, x, y);
yield;
}
}

View File

@ -0,0 +1,575 @@
#TouhouDanmakufu[Player]
#ScriptVersion[3]
#ID["ByakMiko"]
#Title["Byakuren Hijiri & Toyosatomimi no Miko"]
#Text["Unfocused Shot: Indra's Sorcery Buzzsaws[r]An elegant spread of thunder needles.[r][r]Focused Shot: Four Shining Articles[r]A crossing of high-power divine lasers.[r][r]Special Ability: Smaller hitbox."]
//#Image["./marimiko_lib/marimiko_illust.png"]
#ReplayName["ByakMiko"]
#include "script/KevinSystem/Kevin_PlayerLib.txt"
#include "script/KevinSystem/PlayerSoundLib.dnh"
#include "./ByakMiko_Function.dnh"
#include "./ByakMiko_ShotConst.dnh"
#include "script/KevinSystem/kevin_system/Kevin_ItemConst.txt"
#include "script/KevinSystem/kevin_system/Kevin_ItemLib.txt"
let csd = GetCurrentScriptDirectory();
// Global Variables
float maxX = GetStgFrameWidth();
float maxY = GetStgFrameHeight();
// Images & Sound
let teamimg = csd ~ "./playerlib/ByakMiko_Sheet.png";
LoadTextureEx(teamimg, true, true);
//ObjRender_SetTextureFilterMip(teamimg, FILTER_LINEAR);
let sndpath = csd ~ "./sound";
// Other stuff
float playerX = 0;
float playerY = 0;
let objPlayer = GetPlayerObjectID();
int plrender = Obj_GetRenderPriorityI(objPlayer);
int frameidlebyak = 0;
int frameidlemiko = 0;
int framemovebyak = 0;
int framemovemiko = 0;
bool ripplayer = false;
float shotspeed = 0;
float bombrand = 0;
bool bombenable = false;
bool focusactive = false;
bool ishoming = false;
int[] _enemyArray = []; // Prepare an array to store enemy IDs for Kouda's homing shot
int[] _existArray = [];
int[] _shotArray = [];
int grazecounter = 0; // For basic graze = PIV mechanic
float[] PlayerSpd = [11.5, 5.8];
// Custom events for scoring mechanic
const EV_PIV_100 = EV_USER + 100i; // Normal enemies and nons
const EV_PIV_250 = EV_USER + 101i; // Spells
const EV_PIV_500 = EV_USER + 102i; // Last Spells
const EV_PIV_2000 = EV_USER + 103i; // What.
@Initialize{
if(!IsCommonDataAreaExists("PIV")){
CreateCommonDataArea("PIV");
SetAreaCommonData("PIV", "currentvalue", 10000);
}
else{}
SetPlayerStateEndEnable(true);
// Stuff
parameterrender();
playerrender();
Obj_SetRenderPriorityI(objPlayer, 43);
plrender = Obj_GetRenderPriorityI(objPlayer);
_SoundTask();
//SetIntersectionVisualization(true); // Debug
_Mechanic(ripplayer, _enemyArray, _existArray, GetStgFrameWidth(), GetStgFrameHeight(), objPlayer, GetEnemyBossSceneObjectID(), 5, 2, 80);
_HitboxRender(ripplayer, objPlayer, teamimg, teamimg, 2560, 512, 2688, 640, 2816, 1024, 2816+511, 1024+511, 0.22, 0.65);
SetShotAutoDeleteClip(256, 256, 256, 256);
// Shot functions
//_CAVELaser();
ByakShot();
_MikoOption();
// Shot data loading
LoadPlayerShotData(csd ~ "./ByakMiko_ShotData.dnh");
}
@MainLoop{
shotspeed += 1; // Managing the shot rate
//_shotArray = GetAllShotID(TARGET_PLAYER);
//UniversalAlphaHandle(_shotArray);
playerX = ObjMove_GetX(objPlayer);
playerY = ObjMove_GetY(objPlayer);
yield;
}
@Event{
alternative(GetEventType)
// Delete effect
case(EV_DELETE_SHOT_PLAYER){
let graphic = GetEventArgument(2);
float[] position = GetEventArgument(1);
let obj = CreatePlayerShotA1(position[0], position[1], 0, ObjMove_GetAngle(GetEventArgument(0)), 0, 99999, graphic);
ObjShot_SetIntersectionEnable(obj, false);
_DeleteEffect(obj);
}
// PIV-item spawning events
case(EV_PIV_100){
let arg = GetEventArgument(0);
CreatePIVItem(PIV_100, arg[0], arg[1]);
}
case(EV_PIV_250){
let arg = GetEventArgument(0);
CreatePIVItem(PIV_250, arg[0], arg[1]);
}
case(EV_PIV_500){
let arg = GetEventArgument(0);
CreatePIVItem(PIV_500, arg[0], arg[1]);
}
// Basic functionality events
case(EV_REQUEST_SPELL){
let bomb = GetPlayerSpell();
if (bomb >= 1){
SetScriptResult(true);
SetPlayerSpell(bomb - 1);
if(GetVirtualKeyState(VK_SLOWMOVE) != KEY_FREE){_FocusBomb(); _SigilCall(false, teamimg, 2816+256, 512, 2816+512, 768, objPlayer, GetPlayerInvincibilityFrame());}
else{_UnfocusBomb();
_SigilCall(false, teamimg, 2816+256, 512, 2816+512, 768, objPlayer, GetPlayerInvincibilityFrame());
}
}
else {
SetScriptResult(false);
}
}
case(EV_HIT){
ObjSound_Play(predeathsfx);
_DeathbombWarning(teamimg, [1792, 512, 1792+512, 512+512], 15, 0.75);
}
case(EV_PLAYER_SHOOTDOWN){
ObjSound_Play(deathsfx);
ripplayer = true;
_SigilCall(true, teamimg, 2816, 512, 2816+256, 768, objPlayer, 120);
}
case(EV_PLAYER_REBIRTH){
ripplayer = false;
SetPlayerInvincibilityFrame(180);
_SigilCall(false, teamimg, 2816+256, 512, 2816+512, 768, objPlayer, 150);
SetPlayerSpell( max(2,GetPlayerSpell()) );
}
case(EV_GRAZE){
grazecounter += GetEventArgument(0);
ObjSound_Play(grazesfx);
while(grazecounter >= 10){
SetAreaCommonData("PIV", "currentvalue", GetAreaCommonData("PIV", "currentvalue")+10);
grazecounter -= 10;
}
}
}
@Finalize{
}
task _MikoOption(){
// Offsets for 4 front options
float offsetX1 = 90;
float offsetX2 = offsetX1 * 2;
float offsetY1 = 45;
// Array that contains static/unchanging values for the PlayerOption
let val = [1792, 256, 2048, 512, 0.65, 256, 1, 1, false, false, 0, true, false];
// Option handling (right -> rightmost -> left -> leftmost)
float[][] offsetArray = [[offsetX1, offsetY1], [offsetX2, 0], [-offsetX1, offsetY1], [-offsetX2, 0]];
int opt1 = PlayerOption(
offsetArray[0][0], offsetArray[0][1],
teamimg,
val[0], val[1], val[2], val[3], val[4],
val[5], val[6], val[7], val[8],
val[9], val[10],
val[11], val[12]);
int opt2 = PlayerOption(
offsetArray[1][0], offsetArray[1][1],
teamimg,
val[0], val[1], val[2], val[3], val[4],
val[5], val[6], val[7], val[8],
val[9], val[10],
val[11], val[12]);
int opt3 = PlayerOption(
offsetArray[2][0], offsetArray[2][1],
teamimg,
val[0], val[1], val[2], val[3], val[4],
val[5], val[6], val[7], val[8],
val[9], val[10],
val[11], val[12]);
int opt4 = PlayerOption(
offsetArray[3][0], offsetArray[3][1],
teamimg,
val[0], val[1], val[2], val[3], val[4],
val[5], val[6], val[7], val[8],
val[9], val[10],
val[11], val[12]);
int[] optArray = [opt1, opt2, opt3, opt4];
ascent(i in -1..length(optArray)-1){
_SwingBehaviour(optArray[i]);
}
task _SwingBehaviour(target){
float ang = 0;
Obj_SetRenderPriorityI(target, 42);
while(true){
ObjRender_SetAngleZ(target, 0+20*sin(ang));
ang += 360/120;
yield;
}
}
int laser1 = _RenderLaser(opt1, -7, 300, GetStgFrameHeight*1.25, 65, 0, 2.6);
int laser2 = _RenderLaser(opt2, -14, 600, GetStgFrameHeight*1.25, 65, 0, 2.6);
int laser3 = _RenderLaser(opt3, 7, 300, GetStgFrameHeight*1.25, 65, 0, 2.6);
int laser4 = _RenderLaser(opt4, 14, 600, GetStgFrameHeight*1.25, 65, 0, 2.6);
//int laser3 = _RenderLaser(opt3, 0, 300, GetStgFrameHeight*1.25, 50, 0, 5);
float focusDist = 280;
let dummy = CreatePlayerShotA1(playerX, playerY-focusDist, 0, 0, 0, 99999, 0);
ObjShot_SetAutoDelete(dummy, false);
ObjShot_SetIntersectionEnable(dummy, false);
//ObjMove_AddPatternA4(laser1, 0, NO_CHANGE, 0, NO_CHANGE, NO_CHANGE, NO_CHANGE, NO_CHANGE, dummy);
//ObjMove_AddPatternA4(laser2, 0, NO_CHANGE, 0, NO_CHANGE, NO_CHANGE, NO_CHANGE, NO_CHANGE, dummy);
}
// Miko's lasers
// Basic player parameters
task parameterrender(){
SetPlayerItemScope(120);
SetPlayerLife(9); // Debug
SetPlayerSpell(2);
SetPlayerSpeed(PlayerSpd[0], PlayerSpd[1]); // (original: 5.25/2.0)
SetPlayerRebirthFrame(25);
SetPlayerAutoItemCollectLine(GetStgFrameHeight/3);
SetPlayerRebirthLossFrame(0);
ObjPlayer_AddIntersectionCircleA1(objPlayer, 0, 0, 2.0, 55);
}
// Renders the shottype
// Player sprites
task playerrender(){
// Why is this movement code so cursed jesus fucking christ
float scale = 0.34; // Scalies
ObjPrim_SetTexture(objPlayer, teamimg);
ObjSprite2D_SetSourceRect(objPlayer, 0, 0, 320, 384);
ObjSprite2D_SetDestCenter(objPlayer);
Obj_SetRenderPriorityI(objPlayer, 42);
ObjRender_SetScaleXYZ(objPlayer, scale, scale, 1);
// Lower "speed" parameter = FASTER SPEED
// FOR WHEN ONLY IDLE SPRITES ARE DONE
loop{
// Focused
if(GetVirtualKeyState(VK_SLOWMOVE) != KEY_FREE){
frameidlemiko++;
_RenderPlayerMovement(objPlayer, frameidlemiko, 0, 1152, 320, 384, scale, 4, 5);
if (frameidlemiko >= (5*4-1)){frameidlemiko = 0;}
}
// Unfocused
else{
frameidlebyak++;
_RenderPlayerMovement(objPlayer, frameidlebyak, 0, 0, 320, 384, scale, 5, 5);
if (frameidlebyak >= (5*5-1)){frameidlebyak = 0;}
}
yield;
}
}
task ByakShot(){
// Offsets for 4 front options
float offsetX1 = 75;
float offsetX2 = offsetX1 * 2;
float offsetY1 = -75;
// Array that contains static/unchanging values for the PlayerOption
let valL = [2048, 256, 2048+256, 512, 0.32, 256, 1, 1, false, true, 6, false, true];
let valR = [2048, 256, 2048+256, 512, 0.32, 256, 1, 1, false, true, -6, false, true];
// Option handling
float[][] offsetArray = [[offsetX1, offsetY1], [offsetX2, 0], [-offsetX1, offsetY1], [-offsetX2, 0]];
int opt1 = PlayerOption(
offsetArray[0][0], offsetArray[0][1],
teamimg,
valR[0], valR[1], valR[2], valR[3], valR[4],
valR[5], valR[6], valR[7], valR[8],
valR[9], valR[10],
valR[11], valR[12]);
int opt2 = PlayerOption(
offsetArray[1][0], offsetArray[1][1],
teamimg,
valR[0], valR[1], valR[2], valR[3], valR[4],
valR[5], valR[6], valR[7], valR[8],
valR[9], valR[10],
valR[11], valR[12]);
int opt3 = PlayerOption(
offsetArray[2][0], offsetArray[2][1],
teamimg,
valL[0], valL[1], valL[2], valL[3], valL[4],
valL[5], valL[6], valL[7], valL[8],
valL[9], valL[10],
valL[11], valL[12]);
int opt4 = PlayerOption(
offsetArray[3][0], offsetArray[3][1],
teamimg,
valL[0], valL[1], valL[2], valL[3], valL[4],
valL[5], valL[6], valL[7], valL[8],
valL[9], valL[10],
valL[11], valL[12]);
int[] optArray = [opt1, opt2, opt3, opt4];
ObjRender_SetAngleX(opt1, (-1)*valR[4]);
ObjRender_SetAngleX(opt2, (-1)*valR[4]);
ascent(i in -1..length(optArray)-1){
_ThunderNeedle(optArray[i], 270, 7.5, 2);
}
_ThunderNeedle(optArray[1], 135, 7.5, 2);
_ThunderNeedle(optArray[3], 45, 7.5, 2);
//_ThunderNeedle(opt1, 270, 12, 2);
//_ThunderNeedle(opt2, 250, 5, 2);
//_ThunderNeedle(opt4, 290, 5, 2);
//_ThunderNeedle(opt3, 270, 12, 2);
// Byakuren's shot
task _ThunderNeedle(int target, float baseang, float tiltang, int num){
//float baseang = 270;
int a = 0;
loop{
if(IsPermitPlayerShot && !ripplayer && GetVirtualKeyState(VK_SHOT) != KEY_FREE && GetVirtualKeyState(VK_SLOWMOVE) == KEY_FREE){
if(shotspeed % 5 == 0){
ascent(i in 0..num){
float shot = CreatePlayerShotA1(ObjRender_GetX(target), ObjRender_GetY(target), 44, baseang-tiltang+a*tiltang*2, 4.1, 1.8, BYAKUREN_NEEDLE);
ObjRender_SetAlpha(shot, 255*(universalAlpha/100));
_BulletRescalePlayer(shot, 0.7, true, 0.7);
//Fadein(shot);
a++;
if (a == num){a = 0;}
}
ObjSound_Play(basesfx);
}
}
yield;
}
task Fadein(target){
ascent(i in 0..5){
//ObjRender_SetAlpha(target, Interpolate_Decelerate(180, 255, i/5));
_BulletRescalePlayer(target, Interpolate_Decelerate(0.4, 0.7, i/5), true, Interpolate_Decelerate(0.4, 0.7,i/5));
//ObjRender_SetBlendType(target, BLEND_ADD_ARGB);
yield;
}
ObjRender_SetBlendType(target, BLEND_ALPHA);
}
}
}
// Handling of bomb
task Fadein(target, len, targetscale, targetscalehitbox){
ascent(i in 0..len){
//ObjRender_SetAlpha(target, Interpolate_Decelerate(180, 255, i/len));
_BulletRescalePlayer(target, Interpolate_Decelerate(0.4, targetscale, i/len), true, Interpolate_Decelerate(1, targetscalehitbox, i/len));
//ObjRender_SetBlendType(target, BLEND_ADD_ARGB);
yield;
}
ObjRender_SetBlendType(target, BLEND_ALPHA);
}
task _FocusBomb(){
// Preparation
SetForbidPlayerShot(true);
SetForbidPlayerSpell(true);
SetPlayerInvincibilityFrame(32*3+32*3+42*3+120+45);
// Spell object
let manageObj = GetSpellManageObject(); // SPELL BEGINS
ObjSpell_Regist(manageObj);
ObjSound_Play(bombsfx);
SetPlayerSpeed(5, 5);
float angvel = 0.75;
float revolve = 2;
float spd = 21;
float dmg = 2;
float revolvechange = 12;
ascent(i in 0..16){
float angBase = rand(0, 360);
alternative(revolve)
case(2){revolve = -2;}
case(-2){revolve = 2;}
ascent(i in 0..12){
float ang = angBase + 360/12 * i;
int shot = CreateShotA2(playerX, playerY, 24, ang, 0, 24, revolve, 3, 0);
Fadein(shot, 10, 1.5, 1.5);
ObjRender_SetBlendType(shot, BLEND_ADD_ARGB);
ObjShot_SetIntersectionEnable(shot, true);
ObjShot_SetPenetration(shot, 9999);
ObjShot_SetDamage(shot, 2.5);
ObjShot_SetEraseShot(shot, true);
ObjShot_SetSpellFactor(shot, true);
ObjMove_AddPatternA2(shot, 90, NO_CHANGE, NO_CHANGE, 1, 40, 0);
//ObjShot_SetDeleteFrame(shot, 120);
ObjSound_Play(inferno);
}
ascent(i in 0..12){
float ang = angBase + 360/12 * i;
int shot = CreateShotA2(playerX, playerY, 24, ang, 0, 24, -revolve, 3, 0);
Fadein(shot, 10, 1.5, 1.5);
ObjRender_SetBlendType(shot, BLEND_ADD_ARGB);
ObjShot_SetIntersectionEnable(shot, true);
ObjShot_SetPenetration(shot, 9999);
ObjShot_SetDamage(shot, 2.5);
ObjShot_SetEraseShot(shot, true);
ObjShot_SetSpellFactor(shot, true);
ObjMove_AddPatternA2(shot, 90, NO_CHANGE, NO_CHANGE, 1, 40, 0);
//ObjShot_SetDeleteFrame(shot, 120);
ObjSound_Play(inferno);
}
wait(15);
}
// Cleanup, end of spell
SetPlayerSpeed(PlayerSpd[0], PlayerSpd[1]);
SetForbidPlayerShot(false);
wait(60);
SetForbidPlayerSpell(false);
Obj_Delete(manageObj); // !!! IMPORTANT !!!
}
task _UnfocusBomb(){
// Preparation
SetForbidPlayerShot(true);
SetForbidPlayerSpell(true);
SetPlayerInvincibilityFrame(32*3+32*3+42*3+120+45);
// Spell object
let manageObj = GetSpellManageObject(); // SPELL BEGINS
ObjSpell_Regist(manageObj);
ObjSound_Play(bombsfx);
SetPlayerSpeed(10, 10);
float angvel = 0.75;
float revolve = 0;
float spd = 21;
float dmg = 2;
float revolvechange = 12;
ascent(i in 0..100){
int shot = CreateShotA1(rand(0, GetStgFrameWidth()), -125, 36, 90, 2, 0);
Fadein(shot, 18, 1.4, 1.4);
ObjRender_SetBlendType(shot, BLEND_ADD_ARGB);
ObjShot_SetSpinAngularVelocity(shot, 5);
ObjShot_SetIntersectionEnable(shot, true);
ObjShot_SetPenetration(shot, 9999);
ObjShot_SetDamage(shot, 3.5);
ObjShot_SetEraseShot(shot, true);
ObjShot_SetSpellFactor(shot, true);
ObjSound_Play(inferno);
wait(3);
}
// Cleanup, end of spell
SetPlayerSpeed(PlayerSpd[0], PlayerSpd[1]);
SetForbidPlayerShot(false);
wait(60);
SetForbidPlayerSpell(false);
Obj_Delete(manageObj); // !!! IMPORTANT !!!
}
// Screenshake function for bomb's duration - adapted from Sparen's tutorials
task _BombShake(shaketime, intensity){
float baseintensity = intensity;
float shakeno = shaketime;
ascent(i in 0..shakeno){
Set2DCameraFocusX(GetStgFrameWidth/2 + rand(-intensity, intensity));
Set2DCameraFocusY(GetStgFrameHeight/2 + rand(-intensity, intensity));
intensity = Interpolate_Decelerate(0, baseintensity, 1-i/shakeno);
shaketime--;
yield;
}
while(shaketime > 0){yield;}
Set2DCameraFocusX(GetStgFrameWidth/2);
Set2DCameraFocusY(GetStgFrameHeight/2);
yield;
}

View File

@ -0,0 +1,8 @@
let current = GetCurrentScriptDirectory();
let path = current ~ "ByakMiko_ShotData.dnh";
LoadPlayerShotData(path);
// -----
const BYAKUREN_NEEDLE = 1;

View File

@ -0,0 +1,40 @@
shot_image = "./playerlib/ByakMiko_Sheet.png"
ShotData{
id = 0 // Dummy
rect = (0,0,0,0)
render = ALPHA
alpha = 0
collision = 32
}
// Arrow (Option)
ShotData{
id = 1
rect = (2048, 0, 2304, 256)
render = ALPHA
alpha = 255
collision = (75, 0, 0) // Hitbox of arrows is not centered on the sprite
}
// Bomb
ShotData{
id = 2
rect = (2560, 0, 2816, 256)
render = ADD_ARGB
alpha = 255
collision = 80 // Hitbox of arrows is not centered on the sprite
}
ShotData{
id = 3
rect = (2816, 0, 3072, 256)
render = ADD_ARGB
alpha = 255
collision = 80 // Hitbox of arrows is not centered on the sprite
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

View File

@ -0,0 +1 @@
2,0.57,,0.3442,,0.2691,0.3,0.2179,,0.3445,,,,,,,,,,,0.2992,,0.5139,,,1,,,,,,,masterVolume

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,115 @@
task _LaserSpriteRender(
img, int target, float targetAng,
int rectLeft, int rectTop, int rectRight, int rectBottom,
float scaleX, float scaleY, int renderPriority, float alpha,
float scaleSpeed
){
let lasersprite = ObjPrim_Create(OBJ_SPRITE_2D);
ObjPrim_SetTexture(lasersprite, img);
ObjSprite2D_SetSourceRect(lasersprite, rectLeft, rectTop, rectRight, rectBottom);
ObjRender_SetScaleXYZ(lasersprite, 0, scaleY, 1);
ObjSprite2D_SetDestRect(lasersprite, -(rectRight-rectLeft)/2, 0, (rectRight-rectLeft)/2, rectTop-rectBottom);
Obj_SetRenderPriorityI(lasersprite, renderPriority);
ObjRender_SetAlpha(lasersprite, 0);
ObjRender_SetBlendType(lasersprite, BLEND_ADD_ARGB);
async{
loop{
int[] EnemyList = ObjCol_GetListOfIntersectedEnemyID(target);
if(GetVirtualKeyState(VK_SHOT) != KEY_FREE && GetVirtualKeyState(VK_SLOWMOVE) != KEY_FREE && !ripplayer && IsPermitPlayerShot){
// Assumes the intersecting color is right next to the right of the original color
if (length(EnemyList) > 0) {ObjSprite2D_SetSourceRect(lasersprite, rectRight, rectTop, rectRight+(rectRight-rectLeft), rectBottom);}
else{ObjSprite2D_SetSourceRect(lasersprite, rectLeft, rectTop, rectRight, rectBottom);}
float targetx = ObjRender_GetX(target);
float targety = ObjRender_GetY(target);
ObjRender_SetScaleX(lasersprite, min(scaleX+rand(-0.025, 0.025), ObjRender_GetScaleX(lasersprite) + scaleSpeed));
ObjRender_SetAlpha(lasersprite, min(alpha, ObjRender_GetAlpha(lasersprite)+alpha/5));
ObjRender_SetAngleZ(lasersprite, targetAng);
ObjRender_SetPosition(lasersprite, targetx, targety, 1);
yield;
}
else{
ObjRender_SetScaleX(lasersprite, max(0, ObjRender_GetScaleX(lasersprite) - scaleSpeed));
ObjRender_SetAlpha(lasersprite, max(0, ObjRender_GetAlpha(lasersprite)-alpha/5));
yield;
}
yield;
}
}
}
function _RenderLaser(target, float ang, float maxintersectX, float maxLength, float width, float speedLength, float dmg){ /* */
float angRender = asin(maxintersectX/absolute(maxLength));
//int laser = CreatePlayerShotA1(ObjRender_GetX(target), ObjRender_GetY(target), 0, ang, dmg, 9999999, 0);
int laser = CreateStraightLaserA1(ObjRender_GetX(target), ObjRender_GetY(target), ang-90, maxLength, width, 9999999, 0, 1);
ObjLaser_SetInvalidLength(laser, 0, 0);
ObjShot_SetDamage(laser, dmg);
ObjShot_SetAutoDelete(laser, false);
Obj_SetRenderPriorityI(laser, 38);
_Follow(laser, target);
_LaserSpriteRender(
teamimg, laser, ang,
3072, 0, 3072+256, 256,
0.45, 20, 38, 240,
0.14
);
// When shot key is being held, create a line intersection that stretches across the laser.
/*
Calculations:
startx: x of target
starty: y of target
endx: x of laser
endy: maxLength
width = width of laser
*/
async{
loop{
if(GetVirtualKeyState(VK_SHOT) != KEY_FREE && GetVirtualKeyState(VK_SLOWMOVE) != KEY_FREE && !ripplayer && IsPermitPlayerShot){
if(shotspeed % 5 == 0){ObjSound_Play(inferno);}
ObjShot_SetIntersectionEnable(laser, true);
//ObjShot_SetIntersectionLine(laser, ObjRender_GetX(target), ObjRender_GetY(target), ObjRender_GetX(target)+maxintersectX, -maxLength, width);
//WriteLog(ObjMove_GetX(laserfwd));
yield;
}
else{ObjShot_SetIntersectionEnable(laser, false); yield;}
}
}
// After shot key is released, let the laser leave and then delete it.
return laser;
}
task _Follow(follower, followed){
while(!Obj_IsDeleted(follower)){
float x = ObjRender_GetX(followed);
float y = ObjRender_GetY(followed);
ObjMove_SetPosition(follower, x, y);
yield;
}
}

View File

@ -0,0 +1,693 @@
#TouhouDanmakufu[Player]
#ScriptVersion[3]
#ID["RinnoRemi"]
#Title["Rinnosuke Morichika & Remilia Scarlet"]
#Text["Unfocused Shot: Electronic Doombringer[r]Outbursts of fire from the Outside World's doomsday devices.[r][r]Focused Shot: Fate-Sealing Stars[r]High-speed homing stars.[r][r]Special Ability: Longer deathbomb timer."]
//#Image["./mariremi_lib/mariremi_illust.png"]
#ReplayName["RinnoRemi"]
#include "script/KevinSystem/Kevin_PlayerLib.txt"
#include "script/KevinSystem/PlayerSoundLib.dnh"
#include "./RinnoRemi_Function.dnh"
#include "./RinnoRemi_ShotConst.dnh"
#include "script/KevinSystem/kevin_system/Kevin_ItemConst.txt"
#include "script/KevinSystem/kevin_system/Kevin_ItemLib.txt"
let csd = GetCurrentScriptDirectory();
// Global Variables
float maxX = GetStgFrameWidth();
float maxY = GetStgFrameHeight();
// Images & Sound
let teamimg = csd ~ "./playerlib/RinnoRemi_Sheet.png";
LoadTextureEx(teamimg, true, true);
//ObjRender_SetTextureFilterMip(teamimg, FILTER_LINEAR);
let sndpath = csd ~ "./sound";
// Other stuff
float playerX = 0;
float playerY = 0;
let objPlayer = GetPlayerObjectID();
int plrender = Obj_GetRenderPriorityI(objPlayer);
int frameidlerinno = 0;
int frameidleremi = 0;
int framemoverinno = 0;
int framemoveremi = 0;
bool ripplayer = false;
float shotspeed = 0;
float bombrand = 0;
bool bombenable = false;
bool focusactive = false;
bool ishoming = false;
int[] _enemyArray = []; // Prepare an array to store enemy IDs for Kouda's homing shot
int[] _existArray = [];
int[] _shotArray = [];
int grazecounter = 0; // For basic graze = PIV mechanic
float[] PlayerSpd = [9.5, 5.2];
// Custom events for scoring mechanic
const EV_PIV_100 = EV_USER + 100i; // Normal enemies and nons
const EV_PIV_250 = EV_USER + 101i; // Spells
const EV_PIV_500 = EV_USER + 102i; // Last Spells
const EV_PIV_2000 = EV_USER + 103i; // What.
@Initialize{
if(!IsCommonDataAreaExists("PIV")){
CreateCommonDataArea("PIV");
SetAreaCommonData("PIV", "currentvalue", 10000);
}
else{}
SetPlayerStateEndEnable(true);
// Stuff
parameterrender();
playerrender();
Obj_SetRenderPriorityI(objPlayer, 43);
plrender = Obj_GetRenderPriorityI(objPlayer);
_SoundTask();
//SetIntersectionVisualization(true); // Debug
_Mechanic(ripplayer, _enemyArray, _existArray, GetStgFrameWidth(), GetStgFrameHeight(), objPlayer, GetEnemyBossSceneObjectID(), 5, 2, 80);
_HitboxRender(ripplayer, objPlayer, teamimg, teamimg, 2560, 512, 2688, 640, 2816, 1024, 2816+511, 1024+511, 0.22, 0.65);
SetShotAutoDeleteClip(256, 256, 256, 256);
// Shot functions
//_CAVELaser();
rinnoShot();
_BaseShot();
_remiOption();
//UniversalAlphaHandle(_shotArray);
// Shot data loading
LoadPlayerShotData(csd ~ "./RinnoRemi_ShotData.dnh");
}
@MainLoop{
_enemyArray = GetIntersectionRegistedEnemyID;
shotspeed += 1; // Managing the shot rate
//_shotArray = GetAllShotID(TARGET_PLAYER);
//UniversalAlphaHandle(_shotArray);
playerX = ObjMove_GetX(objPlayer);
playerY = ObjMove_GetY(objPlayer);
yield;
}
@Event{
alternative(GetEventType)
// Delete effect
case(EV_DELETE_SHOT_PLAYER){
let graphic = GetEventArgument(2);
float[] position = GetEventArgument(1);
let obj = CreatePlayerShotA1(position[0], position[1], 0, ObjMove_GetAngle(GetEventArgument(0)), 0, 99999, graphic);
ObjShot_SetIntersectionEnable(obj, false); _DeleteEffect(obj);
//if(graphic == ELECTRIC_FIRE_ALT) {_DeleteEffectAlt(obj);}
//else{_DeleteEffect(obj);}
}
// PIV-item spawning events
case(EV_PIV_100){
let arg = GetEventArgument(0);
CreatePIVItem(PIV_100, arg[0], arg[1]);
}
case(EV_PIV_250){
let arg = GetEventArgument(0);
CreatePIVItem(PIV_250, arg[0], arg[1]);
}
case(EV_PIV_500){
let arg = GetEventArgument(0);
CreatePIVItem(PIV_500, arg[0], arg[1]);
}
// Basic functionality events
case(EV_REQUEST_SPELL){
let bomb = GetPlayerSpell();
if (bomb >= 1){
SetScriptResult(true);
SetPlayerSpell(bomb - 1);
if(GetVirtualKeyState(VK_SLOWMOVE) != KEY_FREE){_FocusBomb(); _SigilCall(false, teamimg, 2816+256, 512, 2816+512, 768, objPlayer, GetPlayerInvincibilityFrame());}
else{_UnfocusBomb();
_SigilCall(false, teamimg, 2816+256, 512, 2816+512, 768, objPlayer, GetPlayerInvincibilityFrame());
}
}
else {
SetScriptResult(false);
}
}
case(EV_HIT){
ObjSound_Play(predeathsfx);
_DeathbombWarning(teamimg, [1792, 512, 1792+512, 512+512], 15, 0.75);
}
case(EV_PLAYER_SHOOTDOWN){
ObjSound_Play(deathsfx);
ripplayer = true;
_SigilCall(true, teamimg, 2816, 512, 2816+256, 768, objPlayer, 120);
}
case(EV_PLAYER_REBIRTH){
ripplayer = false;
SetPlayerInvincibilityFrame(180);
_SigilCall(false, teamimg, 2816+256, 512, 2816+512, 768, objPlayer, 150);
SetPlayerSpell( max(2,GetPlayerSpell()) );
}
case(EV_GRAZE){
grazecounter += GetEventArgument(0);
ObjSound_Play(grazesfx);
while(grazecounter >= 10){
SetAreaCommonData("PIV", "currentvalue", GetAreaCommonData("PIV", "currentvalue")+10);
grazecounter -= 10;
}
}
}
@Finalize{
}
// Homing
task _BaseShot(){
loop{
if(IsPermitPlayerShot && !ripplayer && GetVirtualKeyState(VK_SHOT) != KEY_FREE){
if(shotspeed % 5 == 0){
if(GetVirtualKeyState(VK_SLOWMOVE) == KEY_FREE){
float ang2 = 270-2*9;
//float angvel = (3.6-3*0.6)*1.2;
loop(5){
let bullet = CreatePlayerShotA1(playerX, playerY, 32, ang2, 1.4, 1.8, BASE);
Obj_SetRenderPriorityI(bullet, 39);
ObjRender_SetAlpha(bullet, 255*(universalAlpha/100));
ObjShot_SetSpinAngularVelocity(bullet, 10);
_BulletRescalePlayer(bullet, 0.5, true, 0.8);
//angvel -= 0.6*1.2;
ang2 += 9;
}
}
if(GetVirtualKeyState(VK_SLOWMOVE) != KEY_FREE){
float ang2 = 270-2*5;
//float angvel = (3.6-3*0.6)*1.2;
loop(5){
let bullet = CreatePlayerShotA1(playerX, playerY, 32, ang2, 1.2, 1.8, BASE);
Obj_SetRenderPriorityI(bullet, 39);
ObjRender_SetAlpha(bullet, 255*(universalAlpha/100));
ObjShot_SetSpinAngularVelocity(bullet, 10);
_BulletRescalePlayer(bullet, 0.5, true, 0.8);
//angvel -= 0.6*1.2;
ang2 += 5;
}
}
//ObjSound_Play(basesfx);
}
}
yield;
}
}
task _HomeShot(int shot_) {
float duration = 1;
bool homingBool = false;
float basepenetrate = ObjShot_GetPenetration(shot_);
float basedmg = ObjShot_GetDamage(shot_);
//
for (int t = 0i; t < duration && !Obj_IsDeleted(shot_); t++) {
// Checks if enemies are on screen. If enemies are visible, enable homing for the shots.
// _enemyArray is an array containing all enemy IDs, and is constantly updated in @MainLoop.
if (0 < length(_enemyArray)) {
float targetDist = 2000; // Arbitrary number (???)
int targetID = 0;
// Checks distance of every enemy on screen.
for each (int enemy in ref _enemyArray) {
float enemyX = ObjMove_GetX(enemy);
float enemyY = ObjMove_GetY(enemy);
if (0 < enemyX && enemyX < maxX && 0 < enemyY && enemyY < maxY) {
// Returns the hypotenuse of the triangle formed by the x & y distances between the shot and the enemy.
float shotDist = hypot(enemyX - ObjMove_GetX(objPlayer), enemyY - ObjMove_GetY(objPlayer));
// Locks the shot onto the enemy.
if (shotDist < targetDist) {
targetDist = shotDist;
targetID = enemy;
homingBool = true;
}
}
}
// Code to handle the actual homing.
/*if (homingBool) {
for (int f = 0; t < duration && !Obj_IsDeleted(shot_) && !Obj_IsDeleted(targetID) && ObjEnemy_GetInfo(targetID, INFO_LIFE) != 0 && !ObjCol_IsIntersected(shot_); t++) {
ObjShot_SetAutoDelete(shot_, false);
float shotAngle = NormalizeAngle(ObjMove_GetAngle(shot_)); // Angle of the player shot
float targetAngle = NormalizeAngle(atan2(ObjMove_GetY(targetID) - ObjMove_GetY(shot_), ObjMove_GetX(targetID) - ObjMove_GetX(shot_))); // Returns angle from the shot to the enemy.
float angleDistance = AngularDistance(shotAngle, targetAngle); // Angular distance between enemy and player shot
float homeRate = Interpolate_Decelerate(0, 0.75, min(60, f) / 60);
// Homing speed?
ObjMove_SetAngle(shot_, Interpolate_Accelerate(shotAngle, shotAngle + angleDistance, homeRate)); // Interpolate_Necko
f++;
yield;
}
ObjShot_SetAutoDelete(shot_, true);
ObjMove_SetAngularVelocity(shot_, 0);
homingBool = false;
}*/
if (homingBool) {
for (int f = 0; t < duration && !Obj_IsDeleted(shot_) && !Obj_IsDeleted(targetID) && ObjEnemy_GetInfo(targetID, INFO_LIFE) != 0 && !ObjCol_IsIntersected(shot_); t++) {
ObjShot_SetAutoDelete(shot_, false);
float shotAngle = NormalizeAngle(ObjMove_GetAngle(shot_)); // Angle of the player shot
float targetAngle = NormalizeAngle(atan2(ObjMove_GetY(targetID) - ObjMove_GetY(shot_), ObjMove_GetX(targetID) - ObjMove_GetX(shot_))); // Returns angle from the shot to the enemy.
float angleDistance = AngularDistance(shotAngle, targetAngle); // Angular distance between enemy and player shot
float homeRate = Interpolate_Decelerate(0, 0.75, min(60, f) / 60);
// Homing speed?
ObjMove_SetAngle(shot_, Interpolate_Accelerate(shotAngle, shotAngle + angleDistance, 1)); // Interpolate_Necko
f++;
yield;
}
ObjShot_SetAutoDelete(shot_, true);
ObjMove_SetAngularVelocity(shot_, 0);
homingBool = false;
}
}
yield;
}
}
task _SwingBehaviour(target){
float ang = 0;
Obj_SetRenderPriorityI(target, 42);
while(true){
ObjRender_SetAngleZ(target, 0+20*sin(ang));
ang += 360/120;
yield;
}
}
task _remiOption(){
// Offsets for 4 front options
float offsetX1 = 150;
float offsetX2 = offsetX1 * 2;
float offsetY1 = 90;
// Array that contains static/unchanging values for the PlayerOption
let val = [2048, 256, 2304, 512, 0.6, 256, 1, 1, false, false, 0, true, false];
// Option handling (right -> rightmost -> left -> leftmost)
float[][] offsetArray = [[offsetX1, offsetY1], [offsetX2, 0], [-offsetX1, offsetY1], [-offsetX2, 0]];
int opt1 = PlayerOption(
offsetArray[0][0], offsetArray[0][1],
teamimg,
val[0], val[1], val[2], val[3], val[4],
val[5], val[6], val[7], val[8],
val[9], val[10],
val[11], val[12]);
int opt2 = PlayerOption(
offsetArray[1][0], offsetArray[1][1],
teamimg,
val[0], val[1], val[2], val[3], val[4],
val[5], val[6], val[7], val[8],
val[9], val[10],
val[11], val[12]);
int opt3 = PlayerOption(
offsetArray[2][0], offsetArray[2][1],
teamimg,
val[0], val[1], val[2], val[3], val[4],
val[5], val[6], val[7], val[8],
val[9], val[10],
val[11], val[12]);
int opt4 = PlayerOption(
offsetArray[3][0], offsetArray[3][1],
teamimg,
val[0], val[1], val[2], val[3], val[4],
val[5], val[6], val[7], val[8],
val[9], val[10],
val[11], val[12]);
int[] optArray = [opt1, opt2, opt3, opt4];
ascent(i in -1..length(optArray)-1){
_SwingBehaviour(optArray[i]);
_HomingStars(optArray[i]);
}
task _HomingStars(option){
loop{
if(shotspeed % 5 == 0 && GetVirtualKeyState(VK_SHOT) != KEY_FREE && IsPermitPlayerShot && !ripplayer){
if(GetVirtualKeyState(VK_SLOWMOVE) != KEY_FREE){
float x = ObjRender_GetX(option);
float y = ObjRender_GetY(option);
ascent(i in -1..1){
let shotA = CreatePlayerShotA1(-50+x+25-50*i, y, 50, 270, 1.37, 1.25, LOCKON_KNIFE);
//ObjMove_AddPatternA2(shotA, 1, 5, NO_CHANGE, -0.1, 3.75, rand(-0.4, 0.4));
//ObjMove_AddPatternA2(shotA, 1, NO_CHANGE, NO_CHANGE, 25, 50, 0);
_BulletRescalePlayer(shotA, 0.7, true, 1);
ObjRender_SetAlpha(shotA, 255*(universalAlpha/100));
ObjSound_Play(inferno);
_HomeShot(shotA);
//Fadein(shotA, 10);
}
}
}
yield;
}
}
}
// Basic player parameters
task parameterrender(){
SetPlayerItemScope(120);
SetPlayerLife(9); // Debug
SetPlayerSpell(2);
SetPlayerSpeed(PlayerSpd[0], PlayerSpd[1]); // (original: 5.25/2.0)
SetPlayerRebirthFrame(12);
SetPlayerAutoItemCollectLine(GetStgFrameHeight/3);
SetPlayerRebirthLossFrame(0);
ObjPlayer_AddIntersectionCircleA1(objPlayer, 0, 0, 1.25, 40);
}
// Renders the shottype
// Player sprites
task playerrender(){
// Why is this movement code so cursed jesus fucking christ
float scale = 0.39; // Scalies
ObjPrim_SetTexture(objPlayer, teamimg);
ObjSprite2D_SetSourceRect(objPlayer, 0, 0, 320, 384);
ObjSprite2D_SetDestCenter(objPlayer);
Obj_SetRenderPriorityI(objPlayer, 42);
ObjRender_SetScaleXYZ(objPlayer, scale, scale, 1);
// Lower "speed" parameter = FASTER SPEED
// FOR WHEN ONLY IDLE SPRITES ARE DONE
loop{
// Focused
if(GetVirtualKeyState(VK_SLOWMOVE) != KEY_FREE){
frameidleremi++;
_RenderPlayerMovement(objPlayer, frameidleremi, 0, 1152, 320, 384, scale, 4, 6);
if (frameidleremi >= (5*4-1)){frameidleremi = 0;}
}
// Unfocused
else{
frameidlerinno++;
_RenderPlayerMovement(objPlayer, frameidlerinno, 0, 0, 320, 384, scale, 4, 6);
if (frameidlerinno >= (5*4-1)){frameidlerinno = 0;}
}
yield;
}
}
task rinnoShot(){
// Offsets for 4 front options
float offsetX1 = 90;
// Array that contains static/unchanging values for the PlayerOption
let valR = [1792, 256, 2048, 512, 0.5, 256, 1, 1, false, false, 6, false, true];
// Option handling
float[][] offsetArray = [[offsetX1, 0], [-offsetX1, 0]];
/*int opt1 = PlayerOption(
offsetArray[0][0], offsetArray[0][1],
teamimg,
valR[0], valR[1], valR[2], valR[3], valR[4],
valR[5], valR[6], valR[7], valR[8],
valR[9], valR[10],
valR[11], valR[12]);
int opt2 = PlayerOption(
offsetArray[1][0], offsetArray[1][1],
teamimg,
valR[0], valR[1], valR[2], valR[3], valR[4],
valR[5], valR[6], valR[7], valR[8],
valR[9], valR[10],
valR[11], valR[12]);*/
int opt3 = PlayerOption(
0, 150,
teamimg,
valR[0], valR[1], valR[2], valR[3], valR[4],
valR[5], valR[6], valR[7], valR[8],
valR[9], valR[10],
valR[11], valR[12]);
//int[] optArray = [opt1, opt2];
float angle = 255;
/*ascent(i in -1..length(optArray)-1){
_ThunderNeedle(optArray[i], angle, 10, 4);
_ThunderNeedle(optArray[i], 90, 7.5, 2.5);
_SwingBehaviour(optArray[i]);
angle += 35;
}*/
_ThunderNeedle(opt3, 15, 270, 10, 8, 4.2);
_ThunderNeedle(opt3, -15, 90, 15, 4, 4.4);
_SwingBehaviour(opt3);
task _ThunderNeedle(int target, float yoffset, float baseang, float tiltang, int num, float dmg){
//float baseang = 270;
//int a = trunc(-num/2);
// Odd numbers only
loop{
if(IsPermitPlayerShot && !ripplayer && GetVirtualKeyState(VK_SHOT) != KEY_FREE && GetVirtualKeyState(VK_SLOWMOVE) == KEY_FREE){
if(shotspeed % 5 == 0){
ascent(i in trunc(-num/2)..trunc(num/2)+1){
float shot = CreatePlayerShotA1(ObjRender_GetX(target), ObjRender_GetY(target)+yoffset, 65, baseang-tiltang*i, dmg, 1.0, ELECTRIC_FIRE_ALT);
ObjRender_SetAlpha(shot, 255*(universalAlpha/100));
Obj_SetRenderPriorityI(shot, 39);
_BulletRescalePlayer(shot, 0.55, true, 1);
}
}
if(shotspeed % 5 == 0){ObjSound_Play(basesfx);}
}
yield;
}
}
}
task PointblankPlacebo(target, scale, multiplier){
int len = 10;
while(GetObjectDistance(target, objPlayer) < 90){
_BulletRescalePlayer(target, scale*multiplier, true, multiplier); // Pointblank
yield;
}
ascent(i in 0..len){
_BulletRescalePlayer(target, Interpolate_Decelerate(scale*multiplier, scale, i/len), true, Interpolate_Decelerate(multiplier, 1, i/len));
yield;
}
}
task Fadein(target, len){
ascent(i in 0..len){
_BulletRescalePlayer(target, Interpolate_Decelerate(0.4, 0.7, i/len), true, Interpolate_Decelerate(0.4, 0.7, i/len));
//ObjRender_SetBlendType(target, BLEND_ADD_ARGB);
yield;
}
ObjRender_SetBlendType(target, BLEND_ALPHA);
}
task Fadein(target, len, targetscale, targetscalehitbox){
ascent(i in 0..len){
_BulletRescalePlayer(target, Interpolate_Decelerate(0.1, targetscale, i/len), true, Interpolate_Decelerate(1, targetscalehitbox, i/len));
//ObjRender_SetBlendType(target, BLEND_ADD_ARGB);
yield;
}
ObjRender_SetBlendType(target, BLEND_ALPHA);
}
// Handling of bomb
task _FocusBomb(){
// Preparation
SetForbidPlayerShot(true);
SetForbidPlayerSpell(true);
SetPlayerInvincibilityFrame(32*3+32*3+42*3+120+45);
// Spell object
let manageObj = GetSpellManageObject(); // SPELL BEGINS
ObjSpell_Regist(manageObj);
ObjSound_Play(bombsfx);
SetPlayerSpeed(PlayerSpd[1]-2, PlayerSpd[1]-2);
float angvel = 0.75;
float revolve = 0;
float spd = 21;
float dmg = 2;
float revolvechange = 12;
ascent(i in 0..40){
int shot = CreateShotA2(playerX, playerY, 25, rand(260, 280), rand(-1.5, -1), 0.5, HOMING_STAR, 15);
//_BulletRescalePlayer(shot, 3, true, 1);
ObjShot_SetSpinAngularVelocity(shot, 6);
Fadein(shot, 20, 2, 2);
ObjRender_SetBlendType(shot, BLEND_ADD_ARGB);
ObjMove_AddPatternA2(shot, 30, NO_CHANGE, NO_CHANGE, 0.8, 32, 0);
ObjShot_SetIntersectionEnable(shot, true);
ObjShot_SetPenetration(shot, 9999);
ObjShot_SetDamage(shot, 2);
ObjShot_SetEraseShot(shot, true);
ObjShot_SetSpellFactor(shot, true);
ObjSound_Play(inferno);
wait(5);
}
// Cleanup, end of spell
SetPlayerSpeed(PlayerSpd[0], PlayerSpd[1]);
SetForbidPlayerShot(false);
wait(60);
SetForbidPlayerSpell(false);
Obj_Delete(manageObj); // !!! IMPORTANT !!!
}
task _UnfocusBomb(){
// Preparation
SetForbidPlayerShot(true);
SetForbidPlayerSpell(true);
SetPlayerInvincibilityFrame(32*3+32*3+42*3+120+45);
// Spell object
let manageObj = GetSpellManageObject(); // SPELL BEGINS
ObjSpell_Regist(manageObj);
ObjSound_Play(bombsfx);
SetPlayerSpeed(PlayerSpd[0], PlayerSpd[0]);
float angvel = 0.75;
float revolve = 0;
float spd = 21;
float dmg = 2;
float revolvechange = 12;
int hakkero = CreatePlayerShotA1(playerX, playerY, 0, 0, 12, 999999, 4);
ObjShot_SetIntersectionEnable(hakkero, true);
ObjShot_SetEraseShot(hakkero, true);
ObjShot_SetSpellFactor(hakkero, true);
ObjShot_SetSpinAngularVelocity(hakkero, 8);
async{
ascent(i in 0..45){
_BulletRescalePlayer(hakkero, Interpolate_Decelerate(0.5, 2.25, i/45), true, 1);
ObjRender_SetAlpha(hakkero, Interpolate_Decelerate(0, 255, i/45));
yield;
}
}
ascent(i in 0..240){
yield;
}
// Cleanup, end of spell
ObjShot_FadeDelete(hakkero);
SetPlayerSpeed(PlayerSpd[0], PlayerSpd[1]);
SetForbidPlayerShot(false);
wait(60);
SetForbidPlayerSpell(false);
Obj_Delete(manageObj); // !!! IMPORTANT !!!
}
// Screenshake function for bomb's duration - adapted from Sparen's tutorials
task _BombShake(shaketime, intensity){
float baseintensity = intensity;
float shakeno = shaketime;
ascent(i in 0..shakeno){
Set2DCameraFocusX(GetStgFrameWidth/2 + rand(-intensity, intensity));
Set2DCameraFocusY(GetStgFrameHeight/2 + rand(-intensity, intensity));
intensity = Interpolate_Decelerate(0, baseintensity, 1-i/shakeno);
shaketime--;
yield;
}
while(shaketime > 0){yield;}
Set2DCameraFocusX(GetStgFrameWidth/2);
Set2DCameraFocusY(GetStgFrameHeight/2);
yield;
}

View File

@ -0,0 +1,12 @@
let current = GetCurrentScriptDirectory();
let path = current ~ "RinnoRemi_ShotData.dnh";
LoadPlayerShotData(path);
// -----
const HOMING_STAR = 1;
const ELECTRIC_FIRE = 2;
const BASE = 3;
const ELECTRIC_FIRE_ALT = 5;
const LOCKON_KNIFE = 6;

View File

@ -0,0 +1,64 @@
shot_image = "./playerlib/RinnoRemi_Sheet.png"
ShotData{
id = 0 // Dummy
rect = (0,0,0,0)
render = ALPHA
alpha = 0
collision = 32
}
// Homing stars
ShotData{
id = 1
rect = (1792, 0, 2048, 256)
render = ALPHA
alpha = 255
collision = (75, 0, 0) // Hitbox of arrows is not centered on the sprite
}
// Unfocused fire
ShotData{
id = 2
rect = (2048, 0, 2304, 256)
render = ALPHA
alpha = 200
collision = (52, 0, 0) // Hitbox of arrows is not centered on the sprite
}
ShotData{
id = 3
rect = (2304, 0, 2560, 256)
render = ALPHA
alpha = 200
collision = (75, 0, 0) // Hitbox of arrows is not centered on the sprite
}
ShotData{
id = 5
rect = (2560, 0, 2816, 384)
render = ALPHA
alpha = 210
collision = (115, 0, 128) // Hitbox of arrows is not centered on the sprite
}
ShotData{
id = 6
rect = (2816, 0, 3072, 256)
render = ALPHA
alpha = 225
collision = (75, 0, 0) // Hitbox of arrows is not centered on the sprite
}
// Bomb
ShotData{
id = 4
rect = (3328, 1024, 3840, 1536)
render = ADD_ARGB
alpha = 255
collision = 160
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

View File

@ -0,0 +1 @@
2,0.57,,0.3442,,0.2691,0.3,0.2179,,0.3445,,,,,,,,,,,0.2992,,0.5139,,,1,,,,,,,masterVolume

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,322 @@
// Lasers that don't penetrate
// Can be pasted into default player Rumia by adding `TLasers();` to @Initialize
// Activation logic is focused-shooting with a slight delay, similar to CAVE games.
// "RemovePLaserGfx()" can go into shutting-down (probably not necessary).
// Made by razzy
task TLasers{
InitPLaserGfx(); // Probably effects that occur when the laser reaches an enemy? (KEV)
let lasers0 = [ID_INVALID,ID_INVALID,ID_INVALID,ID_INVALID,ID_INVALID,ID_INVALID,ID_INVALID,ID_INVALID,ID_INVALID];
// this delays when the laser fires
// laser fires when laserMode==0
// laserMode increments by 1 per frame
// so laser fires after 15 frames
// KEV: To fire the laser immediately, just set laserMode to 0.
let laserLimit = 15;
let laserMode=-laserLimit;
loop{
if(IsPermitPlayerShot
&& GetPlayerState!=STATE_HIT
&& GetPlayerState!=STATE_DOWN
&& GetPlayerState!=STATE_END
&& (GetVirtualKeyState(VK_SHOT)==KEY_PUSH || GetVirtualKeyState(VK_SHOT)==KEY_HOLD)
){
if(GetVirtualKeyState(VK_SLOWMOVE)==KEY_PUSH || GetVirtualKeyState(VK_SLOWMOVE)==KEY_HOLD){
laserMode=min(0,laserMode+1);
}
else{
// reset laser delay when slowmove is let go
laserMode=-laserLimit;
}
if(laserMode>=0){
let odd = 1;
//NonPenetrationLaser(obj, xoff, yoff, ang, spd, maxLen, dmg, IsStrongLaser, width, vwidth)
// KEV: Ascent loop determines the number of lasers that will be spawned.
ascent(i in 0..1){
let las = lasers0[i];
if(Obj_IsValueExists(las,"DEL") || Obj_IsDeleted(las)){
let magnitude = 1;
lasers0[i]=NonPenetrationLaser(objPlayer, 0, -36, 270, 40, GetStgFrameHeight()*1.5, 2, true, 2, 2);
}
odd=-odd;
}
//PlaySnd(SND_p_shot, 93);
}
}
else{
// reset laser delay when fire is let go
// KEV: Unnecessary if the laser fires immediately
laserMode=-laserLimit;
}
yield;
}
// of an array of 2d coords, return the one closest to (sx,sy). Also returns the distance from (sx,sy) as the third value.
function GetClosestCoord(coords, sx,sy){
let closest=[];
let last_dist=99999;
let arrayLen=length(coords);
if(arrayLen==1){
let cur_dist;
if(length(coords[0])==3){ cur_dist=coords[0][2]; }
else{ cur_dist=((coords[0][0]-sx)^2+(coords[0][1]-sy)^2)^0.5; }
closest=[coords[0][0],coords[0][1], cur_dist];
}
else{
ascent(i in 0..arrayLen){
let cur_dist;
if(length(coords[i])==3){ cur_dist=(coords[i][2]); }
else{ cur_dist=((coords[i][0]-sx)^2+(coords[i][1]-sy)^2)^0.5; }
if(cur_dist < last_dist){
last_dist=cur_dist;
closest=[coords[i][0], coords[i][1], cur_dist];
}
}
}
return closest;
}
// non-penetrating laser object
function NonPenetrationLaser(obj, xoff, yoff, ang, spd, maxLen, dmg, IsStrongLaser, width, vwidth){
/* KEV: Parameter explanations:
obj: Where the laser is fired from
xoff/yoff: x and y offsets relative to the object
ang, spd: Self-explanatory
maxLen: max length of the laser, DON'T SET THIS TOO HIGH! GetStgFrameHeight() + a generous number should be enough
dmg: Self-explanatory
IsStrongLaser: use this if you want to differentiate between strong and weak lasers I guess, I don't play CAVE games so idk
width, vwidth: intersection & render width of laser
*/
// KEV: Defines the laser object.
let damager = ObjShot_Create(OBJ_STRAIGHT_LASER);
ObjShot_SetGraphic(damager, 1);
Obj_SetVisible(damager, false);
ObjShot_Regist(damager);
ObjShot_SetDamage(damager, dmg);
ObjShot_SetPenetration(damager, 8);
ObjLaser_SetLength(damager, 0);
ObjLaser_SetIntersectionWidth(damager, 64*width);
ObjLaser_SetRenderWidth(damager, 64*vwidth);
ObjStLaser_SetAngle(damager, ang);
TNonPenetrationLaser();
return damager;
task TNonPenetrationLaser(){
let scroll=rand(0,64);
let len=0; // the length of the laser (increased until maxLen)
let cosine=cos(ang); // grab this value for placement of the tip. **this assumes the laser never changes angle**
let sine=sin(ang); // grab this value for placement of the tip. **this assumes the laser never changes angle**
// **for lasers that change angle, cosine and sine will need to be updated**
// Laser is now firing
while(!Obj_IsDeleted(damager) // KEV: The four following states can be reduced to !ripplayer in my player scripts
&& IsPermitPlayerShot
&& GetPlayerState!=STATE_HIT
&& GetPlayerState!=STATE_DOWN
&& GetPlayerState!=STATE_END // Reduce these below to != KEY_FREE
&& (GetVirtualKeyState(VK_SHOT)==KEY_PUSH || GetVirtualKeyState(VK_SHOT)==KEY_HOLD)
&& (GetVirtualKeyState(VK_SLOWMOVE)==KEY_PUSH || GetVirtualKeyState(VK_SLOWMOVE)==KEY_HOLD)
){
CheckLaserIntersection;
// Set the laser's position and length
ObjMove_SetPosition(damager, ObjRender_GetX(obj)+xoff,ObjRender_GetY(obj)+yoff);
len = min(maxLen, len+spd);
ObjLaser_SetLength(damager, len);
MakeLaserGfxFrame;
yield;
}
// This laser isn't deleted just yet. I let it fly away first.
// This value lets other tasks check when this happens.
Obj_SetValue(damager, "DEL", true);
// Laser is now leaving
ObjMove_SetAngle(damager, ObjStLaser_GetAngle(damager));
ObjMove_SetSpeed(damager, spd);
while(!Obj_IsDeleted(damager) && len>8){
MakeLaserGfxFrame;
len=max(0,len-spd);
ObjLaser_SetLength(damager, len);
CheckLaserIntersection;
yield;
}
Obj_Delete(damager);
function MakeLaserGfxFrame(){
let hyper=0;
let gLen = max(0, len-32-24);
let gLen2 = max(0, len-32);
// add sprites to the per-frame sprite list
// KEV: Function reference: PLaserGfxFrameLaser(gfxObj, u, v, u2, v2, ry, x, y, ang, sx, sy, rd, gn, bl, al)
/*
KEV: Function description:
Adds a vertex to the attached gfxObj sprite list. Uses u, v, u2, v2 as coordinates for the SourceRect, ry for DestRect (rx is calculated in the function), x & y for vertex position, sx & sy for the scaling, rd, gn and bl for colors, and al for alpha.
PLaserGfxFrame is similar but is used for the base and tip of the laser. The ry (and by extension rx) parameters are removed.
(The color values should be unnecessary.)
*/
// ObjStLaser_SetEndGraphic (ph3sx) may come in handy for the base/tips.
// !IsStrongLaser = 0, IsStrongLaser = 1
PLaserGfxFrameLaser(lasGfxObj[0], 32+hyper*32+!IsStrongLaser*64, scroll, 63+hyper*32+!IsStrongLaser*64, (len-1)/4+scroll, 64, ObjMove_GetX(damager)+cosine*24, ObjMove_GetY(damager)+sine*24, ang+90, vwidth*rand(0.75,1.0),gLen/64, 255, 255, 255, 255);
PLaserGfxFrameLaser(lasGfxObj[1], 32+hyper*32+!IsStrongLaser*64, scroll, 63+hyper*32+!IsStrongLaser*64, (len-1)/4+scroll, 64, ObjMove_GetX(damager)+cosine*24, ObjMove_GetY(damager)+sine*24, ang+90, vwidth*rand(0.75,1.0), gLen/64, 255, 255, 255, 48);
PLaserGfxFrame(lasGfxObj[2], 160, 0, 191, 31, ObjMove_GetX(damager)+cosine*24, ObjMove_GetY(damager)+sine*24, ang+90, vwidth+rand(0.4, 0.5), 6, 255,255,255, 255); // Base of laser
PLaserGfxFrame(lasGfxObj[2], 160, 32, 191, 63, ObjMove_GetX(damager)+cosine*gLen2, ObjMove_GetY(damager)+sine*gLen2, ang+90, vwidth+rand(0.4, 0.5), 6, 255, 255, 255, 255);// Tip of laser
scroll += 3; // Scrolls the laser's graphic.
}
function CheckLaserIntersection(){
// ------------------ Check for enemy collisions ------------------
let enemies=ObjCol_GetListOfIntersectedEnemyID(damager); // Get enemy array
let closest=[];
let arrayLen=length(enemies);
// Check all enemies hit (if any)
if(arrayLen>0){
let enm_pos=[]; // will be a 2-dimensional array
// KEV: If there is no strong/weak laser differentiation, this ascent loop can be removed entirely(?)
ascent(i in 0..arrayLen){ // go through the enemies list
// Weaker lasers get pushed back by "popcorn" enemies.
// KEV: I don't really want to implement strong/weak laser differentiations...
if(!IsStrongLaser || ObjEnemy_GetInfo(enemies[i], INFO_LIFE) > 1){
// There are multiple collisions per enemy to check as well
// It's rare that there's more than one, but it's allowed
let pos=GetEnemyIntersectionPositionByIdA1(enemies[i]); // KEV: Returns the multiple hitboxes of the enemy as a 2D array, format is [index][x, y of hitbox].
/* KEV: Further explanation (rough and probably incorrect);
An enemy has 2 hitboxes, one at coords [16, 16] and one at [32, 32].
You get a 2D array named "coords" containing the coordinates of these 2 hitboxes by using GetEnemyIntersectionPositionByIdA1.
You then write this line "float num = coords[0][1];" and WriteLog() the value of num.
coords[0][1] corresponds to the y coordinate of the first hitbox, which would give you a num value of 16.
(I don't know the order the hitboxes will be sorted in the array, though. For all I know, coords[0][1] may be the second hitbox's y (32)...?)
*/
let closest2=GetClosestCoord(pos, ObjMove_GetX(damager),ObjMove_GetY(damager));
if(closest2[0]!=-1234){
enm_pos=enm_pos~[closest2];
}
}
}
closest = GetClosestCoord(enm_pos, ObjMove_GetX(damager),ObjMove_GetY(damager));
}
// ------------------ ------------------ ------------------
// There has been a collision, dial back laser length to (roughly) the point of collision.
// (Roughly) because we can't get the exact location, and doing it ourselves requires finding the hitbox dimensions.
// KEV: Getting the hitbox dimensions/locations should be perfectly possible with ph3sx's intersection-obtaining functions. Will need re-examining
if(length(closest) > 0){
let dist=closest[2]-16;
len=max(0,dist);
}
}
}
} // NonPenetrationLaser
}
let lasGfxObj=[]; // KEV: An array that will contain three sprite lists, rendering three different parts of the laser (base, body, tip).
// KEV: Creates the sprite lists, assigns the texture to them, and adds them as indexes into lasGfxObj.
task InitPLaserGfx(){
let imgLaser = GetCurrentScriptDirectory() ~ "laser_fx.png";
LoadTexture(imgLaser);
ascent(i in 0..3){
let gfx = ObjPrim_Create(OBJ_SPRITE_LIST_2D);
ObjPrim_SetTexture(gfx, imgLaser);
Obj_SetRenderPriorityI(gfx, 41);
lasGfxObj = lasGfxObj~[gfx];
}
ObjRender_SetBlendType(lasGfxObj[1], BLEND_ADD_ARGB);
Obj_SetRenderPriorityI(lasGfxObj[2], 42);
while(true){
ascent(i in 0..length(lasGfxObj)){
ObjSpriteList2D_ClearVertexCount(lasGfxObj[i]);
}
yield;
}
}
task RemovePLaserGfx{
let imgLaser = GetCurrentScriptDirectory() ~ "laser_fx.png";
RemoveTexture(imgLaser);
ascent(i in 0..length(lasGfxObj)){
Obj_Delete(lasGfxObj[i]);
lasGfxObj[i]=ID_INVALID;
}
}
// See the non-penetrating laser object task for information on these tasks.
task PLaserGfxFrame(gfxObj, u, v, u2, v2, x, y, ang, sx, sy, rd, gn, bl, al){
if(gfxObj==ID_INVALID){return;}
ObjRender_SetPosition(gfxObj, x, y, 0);
ObjRender_SetAngleZ(gfxObj, ang);
ObjRender_SetScaleXYZ(gfxObj, sx, sy, 1);
ObjRender_SetColor(gfxObj, rd, gn, bl);
ObjRender_SetAlpha(gfxObj, al);
ObjSpriteList2D_SetSourceRect(gfxObj, u, v, u2, v2);
ObjSpriteList2D_SetDestCenter(gfxObj);
ObjSpriteList2D_AddVertex(gfxObj);
}
task PLaserGfxFrameLaser(gfxObj, u, v, u2, v2, ry, x, y, ang, sx, sy, rd, gn, bl, al){
if(gfxObj==ID_INVALID){return;}
ObjRender_SetPosition(gfxObj, x,y,0);
ObjRender_SetAngleZ(gfxObj, ang);
ObjRender_SetScaleXYZ(gfxObj, sx ,sy, 1);
ObjRender_SetColor(gfxObj, rd,gn,bl);
ObjRender_SetAlpha(gfxObj, al);
ObjSpriteList2D_SetSourceRect(gfxObj, u, v, u2, v2);
let rx=(u2-u)/2;
ObjSpriteList2D_SetDestRect(gfxObj, -rx,0,rx,-ry);
ObjSpriteList2D_AddVertex(gfxObj);
}