Compare commits

...

2 Commits

37 changed files with 132 additions and 68 deletions

View File

@ -2,6 +2,9 @@
Repository for Kevinmonitor and co.'s Touhou Station Jam 3 game.
虹魔郷
The game is now fully playable.
All attacks are now available as of 22/8/2022.
+ Remilia Nonspell 1
@ -18,6 +21,11 @@ How to install:
+ Install all files in this repo.
+ Open either th_dnh_ph3sx.exe or th_dnh_ph3sx_legacy.exe (if you cannot open the latter)
To play the full game (with options, language selection, menus, difficulty select, etc.)
+ Select "Package" in the title screen.
+ Select "be gay do crimes". Yes, this is the actual name of the script.
To play attacks individually:
+ Select "Single" in the title screen.

View File

@ -1,29 +1,65 @@
Game Description (on Game Page):
[The Japanese description is translated using DeepL. I am sorry for any mistakes.]
ENGLISH:
A fan-made side story to the brand new instalment of Touhou Project, “100th Black Market”, starring the heroic Chimata Tenkyuu.
A black market at the Scarlet Devil Mansion has opened up! Play as the market god and face the mischievous vampire using the cards for wicked purposes!
Read the in-game manual for more information!
Controls:
Z (Hold): Shoot/Confirm
X: Use Spell Card (limited use)/Cancel
Esc: Pause Game
Esc+R: Quick Retry
Esc+Q: Quick Exit to Title
See config.exe to rebind keys.
Credits:
Game Programmer & Designer
Character & Background Artwork
Spritework
Graphic Design & Artwork
Kevin Minh (@kevinminh_alt)
Spritework & Card Artwork
Japanese Translation
Additional Artwork
Aqwa (@aqwwa58)
Schmagons
Composer (Boss Theme)
Yal (@GameDevYal)
JAPANESE:
東方Projectの完全新作「100番目の闇市」のファンメイドサイドストーリーで、主人公の「天弓千亦」が主演します。
紅魔館に闇市が開かれた!?マーケットの神様として、カードを悪用する吸血鬼に立ち向かえ!
操作方法
Z (ホールド): 撃つ/確認する
X: スペルカード使用(使用制限あり/キャンセル
Esc: ゲームの一時停止
Esc+R: クイックリトライ
Esc+Q: タイトル画面でクイック終了
キーのリバインドはconfig.exeを参照してください。
クレジット
ゲームプログラマー&デザイナー
グラフィックデザイン&アートワーク
Kevin Minh (@kevinminh_alt)
追加アートワーク
Aqwa (@aqwwa58)
Schmagons
作曲家 (ボスのテーマ)
Yal (@GameDevYal)
[INSERT JP TRANSLATION HERE]
——

Binary file not shown.

View File

@ -58,7 +58,7 @@ Lifebar full = (5, 78, 16, 108)
StartItemScript(dirCurrent ~ "./kevin_system/KevinSystem_Item.txt");
//TExtendSystem();
TExtendSystem();
TBossLife();
TBossTimer();
TCurrentFps();
@ -131,7 +131,7 @@ task TExtendSystem(){
//ObjText_SetFontBold(NextText, true);
// Indicates the score threshold needed for the next extend
let ExtendThresholdText =
/*let ExtendThresholdText =
CreateTextObject(
250, 960, 24,
@ -142,7 +142,7 @@ task TExtendSystem(){
);
ObjText_SetFontBold(ExtendThresholdText, true);
ObjText_SetHorizontalAlignment(ExtendThresholdText, ALIGNMENT_RIGHT);
ObjText_SetHorizontalAlignment(ExtendThresholdText, ALIGNMENT_RIGHT);*/
int count = 0;
// Millions
@ -150,8 +150,8 @@ task TExtendSystem(){
];
if(GetCommonData("Difficulty", "Hard") == "Hard"){req = [20, 80, 200, 500, 800, 1200, 2500, 5000];}
else{req = [15, 50, 100, 180, 300, 600, 1000, 2000, 4000];}
if(GetCommonData("Difficulty", "Normal") == "Normal"){req = [10, 20, 40, 70];}
else{req = [10, 20, 40, 70];}
let next = 0;
@ -165,8 +165,8 @@ task TExtendSystem(){
ExtendEffect;
}
}
if(next != 8){ObjText_SetText(ExtendThresholdText, "NEXT: " ~ DigitToCommaArray(req[next]*1000000));}
else{ObjText_SetText(ExtendThresholdText, "MAX EXTEND");}
/*if(next != 8){ObjText_SetText(ExtendThresholdText, "NEXT: " ~ DigitToCommaArray(req[next]*1000000));}
else{ObjText_SetText(ExtendThresholdText, "MAX EXTEND");}*/
yield;
}
}

View File

@ -17,21 +17,23 @@
*/
task _InitDifficulty(int diffInt){
function <int> _InitDifficulty(int diffInt){
int difficulty = 0;
if(GetCommonData("Difficulty", "Normal") == "Normal"){
diffInt = 0;
difficulty = 0;
}
else if(GetCommonData("Difficulty", "Normal") == "Hyper"){
diffInt = 1;
else if(GetCommonData("Difficulty", "Normal") == "Hard"){
difficulty = 1;
}
else if(GetCommonData("Difficulty", "Normal") == "Unparalleled"){
diffInt = 2;
else if(GetCommonData("Difficulty", "Normal") == "Lunatic"){
difficulty = 2;
}
return;
return difficulty;
}
// Explosion Sound Effects
@ -258,7 +260,7 @@ task _GoToBrazil(
if(ObjEnemyBossScene_GetInfo(targetscene, INFO_PLAYER_SHOOTDOWN_COUNT) > 0){
finalItemDrop = minitemdrop;
itemType = POINT_REGULAR;
//itemType = POINT_REGULAR;
}
DeleteShotAll(TYPE_ALL,TYPE_ITEM);

View File

@ -29,14 +29,14 @@ InstallFont("script/KevinSystem/font/AnkeCall.ttf");
// Placeholder
let invalid = "script/KevinSystem/img/lol.png";
let testEnemyTex = "script/game/StageLib/TestEnemy.png";
let testEnemyTex = "script/game/resourceLib/Spritesheet_StationJam.png";
LoadTextureEx(invalid, true, true);
LoadTextureEx(testEnemyTex, true, true);
int PetalEffect = ObjParticleList_Create(OBJ_PARTICLE_LIST_2D);
_EffectListPreRender(PetalEffect, testEnemyTex, [512, 0, 768, 256]);
_EffectListPreRender(PetalEffect, testEnemyTex, [1536, 0, 1792, 256]);
_SoundTask;
// Simple text object creation (adapted from mkm)

Binary file not shown.

View File

@ -27,9 +27,9 @@ int STGHEIGHT = GetStgFrameHeight();
task TBackgroundNew(renderX, renderY){
let objText = ObjText_Create();
ObjText_SetText(objText, "Thank you for playing![r]Until the next pride...");
ObjText_SetFontSize(objText, 36);
ObjText_SetFontType(objText, "Connecting Chain Handserif");
ObjText_SetText(objText, ["Thank you for playing![r]Until the next station...", "このゲームを遊ぶをありがとうございました!"][GetCommonData("Language", 0)]);
ObjText_SetFontSize(objText, [38, 45][GetCommonData("Language", 0)]);
ObjText_SetFontType(objText, "コーポレート・ロゴ(ラウンド) ver2 Bold");
//ObjText_SetMaxWidth(objText, GetScreenWidth/2);
ObjText_SetHorizontalAlignment(objText, ALIGNMENT_CENTER);
@ -185,7 +185,7 @@ task TMenu
let mx = GetScreenWidth/2;
let my = GetScreenHeight/4;
let texts = ["Save A Replay", "Depart From The Market", "Re-enter The Parade"];
let texts = ["Save Replay", "Back To Title", "Restart Battle"];
var countMenu = length(texts);
ascent(var iText in 0 .. countMenu)
{

View File

@ -31,19 +31,27 @@ task TFunFact(){
//int y = 0;
let textchoices = [
"The sun is just a really hot egg tart, if you think about it.",
"Gay people.",
"You know what else is as cool as gay Touhous? It's Kouji Kouda[r]from the hit anime \"Boku no Hero Academia\"- *turns into a Kouda plushie*",
"Thanks for playing this game! I hope you enjoyed my gay Touhou endeavours.",
"You can set the amount of starting lives you have in the configurations menu.[r]If you have trouble with difficult patterns or stage sections, now you can blast right through them!"
["The sun is just a really hot egg tart, right?",
"Touhou.",
"You know what else is as cool as Touhou? It's Kouji Kouda[r]from the hit anime \"Boku no Hero Academia\"!",
"Thanks for playing this game!",
"You can set the amount of starting lives you have in the configurations menu.[r]If you have trouble with difficult patterns or stage sections, now you can blast right through them!"],
["太陽は実際に暑いエッグタルト、ね?",
"東方.",
"このゲームの開発元の一番好きなキャラクターは『僕のヒーローアカデミア』の『口田甲司』ですよ![r]口田くんはすごいとかわいいね...",
"このゲームを遊ぶをありがとうございました!",
"あなたは『OPTIONS』のメニューにはライフカウンターを変化することができます。"],
];
int x = prand_int(0, length(textchoices)-1);
int x = prand_int(0, length(textchoices[GetCommonData("Language", 0)])-1);
//y = length(textchoices);
let objText = ObjText_Create();
ObjText_SetText(objText, textchoices[x]);
ObjText_SetFontSize(objText, 36);
ObjText_SetFontType(objText, "Connecting Chain Handserif");
ObjText_SetText(objText, textchoices[GetCommonData("Language", 0)][x]);
ObjText_SetFontSize(objText, [38, 45][GetCommonData("Language", 0)]);
ObjText_SetFontType(objText, "コーポレート・ロゴ(ラウンド) ver2 Bold");
//ObjText_SetMaxWidth(objText, GetScreenWidth/2);
ObjText_SetHorizontalAlignment(objText, ALIGNMENT_CENTER);
@ -205,7 +213,7 @@ task TMenu
let mx = GetScreenWidth/2;
let my = GetScreenHeight/4;
let texts = ["Resume The Faceoff", "Accept Your Losses", "Take Another Shot"];
let texts = ["Resume Game", "Back To Title", "Restart Battle"];
var countMenu = length(texts);
ascent(var iText in 0 .. countMenu)
{

View File

@ -38,6 +38,8 @@ float BGMRate = GetAreaCommonData("Config", "BGMVol", 100) * 0.01;
}
else{}
SetPlayerLife(GetAreaCommonData("Config", "StartingLife", 5));
SetAutoDeleteObject(true);
ObjSound_Load(objBGM, "script/game/resourceLib/bossTheme.ogg");
@ -86,6 +88,11 @@ task PluralTask(){
bossTrueStart = true;
ObjSound_Play(objBGM);
async{
while(GetPlayerState() != STATE_END){yield;}
ObjSound_Stop(objBGM);
}
while(!Obj_IsDeleted(obj)){
yield;
}

View File

@ -38,9 +38,9 @@ int[] denseBubble = [8, 9, 10];
int[] delayBubble = [15, 12, 10];
float[] speedBubble = [8, 9, 9];
int[] denseCurve = [5, 7, 7];
float[] offsetCurve = [2, 2.8, 4.2];
int[] denseSpiral = [6, 8, 9];
int[] denseCurve = [5, 6, 7];
float[] offsetCurve = [2, 2.4, 4.25];
int[] denseSpiral = [6, 7, 8];
int[] delaySpiral = [10, 8, 10];
int[] speedSpiral = [6, 8, 8];
@ -55,7 +55,7 @@ int[] speedSpiral = [6, 8, 8];
SetAutoDeleteObject(true);
_InitDifficulty(difficultySelect);
difficultySelect = _InitDifficulty(difficultySelect);
//difficultySelect = 2; // debug

View File

@ -58,7 +58,7 @@ int[] delaySpiral = [5, 5, 4];
SetAutoDeleteObject(true);
_InitDifficulty(difficultySelect);
difficultySelect = _InitDifficulty(difficultySelect);
//difficultySelect = 0; // debug

View File

@ -88,7 +88,7 @@ int[] thresholdStopSpin = [80, 70, 60];
SetAutoDeleteObject(true);
_InitDifficulty(difficultySelect);
difficultySelect = _InitDifficulty(difficultySelect);
difficultySelect = 1; // debug

View File

@ -38,16 +38,16 @@ Parameters:
int[] denseSpiral = [6, 7, 8];
float[] angleoffsetSpiral = [1.618*7, 1.618*6, 1.618*5];
float[] speedSpiral = [8, 9.25, 11];
float[] speedSpiral = [8.25, 9.5, 11];
int[] denseRing = [10, 12, 14];
float[] maxspeedRing = [7.5, 8.5, 10];
float[] maxspeedRing = [7.5, 9, 10];
int[] deceltimeRing = [20, 25, 32];
int[] acceltimeRing = [60, 50, 40];
// Remilia takes 36 frames. Preferably, pick a number 36 can divide with.
int[] delayRing = [9, 6, 5];
int[] delayRing = [6, 5, 3];
// How much time Remilia takes to charge her attack and you get to GTFO.
@ -67,7 +67,7 @@ string tex = "script/game/resourceLib/Spritesheet_StationJam.png";
SetAutoDeleteObject(true);
LoadTextureEx(tex, true, false);
_InitDifficulty(difficultySelect);
difficultySelect = _InitDifficulty(difficultySelect);
//difficultySelect = 2; // debug
@ -214,7 +214,7 @@ function <void> _VampireChase(){
float ang = GetAngleToPlayer(bossObj);
loop(36/delayRing){
loop(36/delayRing[difficultySelect]){
ascent(i in 0..denseRing[difficultySelect]){
int shot = CreateShotA2(bossX, bossY, maxspeedRing[difficultySelect], ang + 360/denseRing[difficultySelect] * i, -maxspeedRing[difficultySelect]/deceltimeRing[difficultySelect], 0, 0, KEV_AURABALL_RED, 10);
@ -228,7 +228,7 @@ function <void> _VampireChase(){
}
ang += 18;
wait(delayRing);
wait(delayRing[difficultySelect]);
}

View File

@ -40,21 +40,21 @@ Parameters:
*/
int[] denseLine = [3, 4, 5];
int[] speedLine = [8.5, 10, 12];
float[] delayLine = [23, 20, 18];
int[] speedLine = [9, 10, 12];
float[] delayLine = [20, 18, 15];
int[] denseRing = [10, 12, 14];
int[] denseRing = [12, 14, 15];
float[] maxspeedRing = [8, 9, 11];
int[] deceltimeRing = [20, 30, 40];
int[] deceltimeRing = [20, 25, 30];
int[] acceltimeRing = [60, 45, 35];
// Remilia takes 36 frames to charge down the screen. Preferably, pick a number 36 can divide with.
int[] ringDelay = [12, 9, 6];
int[] ringDelay = [4, 3, 3];
// How much time Remilia takes to charge her attack and you get to GTFO.
int[] chargetimeRemi = [75, 60, 50];
int[] chargetimeRemi = [60, 45, 40];
float[] maxspeedBookAim = [10, 12, 15];
int[] bookArray = []; // it's a secret tool that will help us later
@ -73,9 +73,9 @@ string tex = "script/game/resourceLib/Spritesheet_StationJam.png";
SetAutoDeleteObject(true);
LoadTextureEx(tex, true, false);
_InitDifficulty(difficultySelect);
difficultySelect = _InitDifficulty(difficultySelect);
//difficultySelect = 0; // debug
//difficultySelect = 2; // debug
SetShotAutoDeleteClip(64, 64, 64, 64);
@ -272,7 +272,7 @@ function <void> ChargeAttack(float x, int colorselect){
x, 0,
90, 90,
3333, 3333,
0, STG_WIDTH/6,
0, [STG_WIDTH/6, STG_WIDTH/6+60, 3*STG_WIDTH/6][difficultySelect],
0xDC7171, 220,
15, chargetimeRemi[difficultySelect], 15
);
@ -307,7 +307,7 @@ function <void> ChargeAttack(float x, int colorselect){
int color = [KEV_AURABALL_RED, KEV_AURABALL_AQUA, KEV_AURABALL_ORANGE, KEV_AURABALL_WHITE, KEV_AURABALL_GREEN, KEV_AURABALL_PINK][colorselect];
loop(36/ringDelay){
loop(36/ringDelay[difficultySelect]){
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);
@ -319,7 +319,7 @@ function <void> ChargeAttack(float x, int colorselect){
//Shoot1;
}
}
wait(ringDelay);
wait(ringDelay[difficultySelect]);
}
@ -331,7 +331,7 @@ function <void> ChargeAttack(float x, int colorselect){
wait(15);
ObjEnemy_SetDamageRate(bossObj, 100, 100);
wait(75);
wait([75, 70, 65][difficultySelect]);
}
@ -370,7 +370,7 @@ task CreateBook(
_BulletRescale(shot, 0.85, true, 1);
Shoot1;
}
wait([3, 4, 4][difficultySelect]);
wait([5, 4, 3][difficultySelect]);
}
wait(delayLine[difficultySelect]);

View File

@ -62,7 +62,7 @@ string tex = "script/game/resourceLib/Spritesheet_StationJam.png";
SetAutoDeleteObject(true);
LoadTextureEx(tex, true, false);
_InitDifficulty(difficultySelect);
difficultySelect = _InitDifficulty(difficultySelect);
//difficultySelect = 0; // debug

View File

@ -88,7 +88,7 @@ int[] thresholdStopSpin = [80, 70, 60];
SetAutoDeleteObject(true);
_InitDifficulty(difficultySelect);
difficultySelect = _InitDifficulty(difficultySelect);
//difficultySelect = 1; // debug

View File

@ -76,6 +76,8 @@ function <void> Dialogue(){
//wait(60);
TTextScroll(objText2, "KEEP YOUR SEXINESS");
wait(60);
Obj_Delete(objText);
Obj_Delete(objText2);
Obj_Delete(objText3);

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.

Before

Width:  |  Height:  |  Size: 403 KiB

After

Width:  |  Height:  |  Size: 447 KiB

View File

@ -54,6 +54,7 @@ int[] _shotArray = [];
int grazecounter = 0; // For basic graze = PIV mechanic
int shotAlpha = (GetAreaCommonData("Config", "PlayerShotOpacity", 60)*0.01)*255;
float[] PlayerSpd = [10, 5.35];
// Custom events for scoring mechanic
@ -506,7 +507,7 @@ task _ShotType(){
ascent(i in -1..1){
let shotA = CreatePlayerShotA1(-50+x+25-50*i, y, 50, ObjMove_GetAngle(option), 2, 1.1, 2);
_BulletRescalePlayer(shotA, 0.7, true, 1);
ObjRender_SetAlpha(shotA, 255*(universalAlpha/100));
ObjRender_SetAlpha(shotA, shotAlpha);
Obj_SetRenderPriorityI(shotA, 39);
ObjSound_Play(basesfx);
@ -577,7 +578,7 @@ task _ShotType(){
ascent(i in -1..1){
let shotA = CreatePlayerShotA1(-50+x+25-50*i, y, 50, ObjMove_GetAngle(option), 2.6, 1.1, 1);
_BulletRescalePlayer(shotA, 0.7, true, 1);
ObjRender_SetAlpha(shotA, 255*(universalAlpha/100));
ObjRender_SetAlpha(shotA, shotAlpha);
Obj_SetRenderPriorityI(shotA, 39);
ObjSound_Play(basesfx);

View File

@ -24,7 +24,7 @@ ShotData{
id = 2
rect = (512, 512, 768, 896)
render = ALPHA
alpha = 200
alpha = 255
collision = (128, 0, 74) // Hitbox of arrows is not centered on the sprite
}

View File

@ -6,7 +6,7 @@
//--------------------------------------------------------------
//Window Title
// The text to display on the window title.
//window.title = ExRumia Package
//window.title = ~ Scarlet Black Market
//--------------------------------------------------------------
//Screen Size
@ -25,7 +25,7 @@ window.size.list = 960x540 1280x720 1920x1080
//Skip Mode Speed
// The speed multiplier of the skip mode. (Triggered while Left CTRL is held down by default.)
// Limited between 1 and 50.
skip.rate = 20
skip.rate = 0
//--------------------------------------------------------------
//Enable/Disable Unfocused Processing