#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; }