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