diff --git a/demos/README.md b/demos/README.md index 2cb83dc89..32bb0fa69 100644 --- a/demos/README.md +++ b/demos/README.md @@ -14,6 +14,7 @@ these demo songs are not under the GPL. all rights are reserved to the original - Aishi Tsukumo - akumanatt - aloelucidity +- ALTMUS - AmigaX - AquaDoesStuff - asikwus diff --git a/demos/snes/time_of_the_dreamwatch.fur b/demos/snes/time_of_the_dreamwatch.fur new file mode 100644 index 000000000..22cb516d8 Binary files /dev/null and b/demos/snes/time_of_the_dreamwatch.fur differ diff --git a/extern/backward/backward.hpp b/extern/backward/backward.hpp index 096e9d73d..d5dc5d73e 100644 --- a/extern/backward/backward.hpp +++ b/extern/backward/backward.hpp @@ -24,6 +24,8 @@ #ifndef H_6B9572DA_A64B_49E6_B234_051480991C89 #define H_6B9572DA_A64B_49E6_B234_051480991C89 +extern int curEngineState; + #ifndef __cplusplus #error "It's not going to compile without a C++ compiler..." #endif @@ -4253,7 +4255,11 @@ public: } #ifdef _WIN32 - MessageBox(NULL,"Error","Furnace has crashed! please report this to the issue tracker immediately:\r\nhttps://github.com/tildearrow/furnace/issues/new\r\n\r\na file called furnace_crash.txt will be created in your user directory.\r\nthis will be important for locating the origin of the crash.\r\n\r\nif Furnace keeps crashing and you believe it is caused by a configuration problem, you may start Furnace with the -safemode parameter.",MB_OK|MB_ICONERROR); + if (curEngineState==7 || curEngineState==11) { + MessageBox(NULL,"Furnace whoopsied and obliterated itself to pieces!\r\n\r\nreport the issue to tildearrow with the provided \"furnace_crash.txt\" in your home folder, or whatever that happened to you is inevitable sorry : < < < <\r\n\r\nor do -safemode in terminal if you got the bravery to do so\r\n\r\nif it also still crashes i'm afraid to tell you this is an other undercooked Furnace update","CRASHED IMMENSELY",MB_OK|MB_ICONERROR); + } else { + MessageBox(NULL,"Furnace has crashed! please report this to the issue tracker immediately:\r\nhttps://github.com/tildearrow/furnace/issues/new\r\n\r\na file called furnace_crash.txt will be created in your user directory.\r\nthis will be important for locating the origin of the crash.\r\n\r\nif Furnace keeps crashing and you believe it is caused by a configuration problem, you may start Furnace with the -safemode parameter.","Error",MB_OK|MB_ICONERROR); + } std::string crashLocation; char* userProfile=getenv("USERPROFILE"); if (userProfile==NULL) { @@ -4492,7 +4498,11 @@ private: printer.print(st, std::cerr); #ifdef _WIN32 - MessageBox(NULL,"Furnace has crashed! please report this to the issue tracker immediately:\r\nhttps://github.com/tildearrow/furnace/issues/new\r\n\r\na file called furnace_crash.txt will be created in your user directory.\r\nthis will be important for locating the origin of the crash.\r\n\r\nif Furnace keeps crashing and you believe it is caused by a configuration problem, you may start Furnace with the -safemode parameter.","Error",MB_OK|MB_ICONERROR); + if (curEngineState==7 || curEngineState==11) { + MessageBox(NULL,"Furnace whoopsied and obliterated itself to pieces!\r\n\r\nreport the issue to tildearrow with the provided \"furnace_crash.txt\" in your home folder, or whatever that happened to you is inevitable sorry : < < < <\r\n\r\nor do -safemode in terminal if you got the bravery to do so\r\n\r\nif it also still crashes i'm afraid to tell you this is an other undercooked Furnace update","CRASHED IMMENSELY",MB_OK|MB_ICONERROR); + } else { + MessageBox(NULL,"Furnace has crashed! please report this to the issue tracker immediately:\r\nhttps://github.com/tildearrow/furnace/issues/new\r\n\r\na file called furnace_crash.txt will be created in your user directory.\r\nthis will be important for locating the origin of the crash.\r\n\r\nif Furnace keeps crashing and you believe it is caused by a configuration problem, you may start Furnace with the -safemode parameter.","Error",MB_OK|MB_ICONERROR); + } std::string crashLocation; char* userProfile=getenv("USERPROFILE"); if (userProfile==NULL) { diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 54e0c96ba..11bd9f7f6 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -42,6 +42,8 @@ #include #include +int curEngineState=-1; + void process(void* u, float** in, float** out, int inChans, int outChans, unsigned int size) { ((DivEngine*)u)->nextBuf(in,out,inChans,outChans,size); } @@ -4018,6 +4020,31 @@ bool DivEngine::prePreInit() { logD("config path: %s",configPath.c_str()); configLoaded=true; + curEngineState=-1; + time_t thisMakesNoSense=time(NULL); + struct tm curTime; +#ifdef _WIN32 + struct tm* tempTM=localtime(&thisMakesNoSense); + if (tempTM!=NULL) { + memcpy(&curTime,tempTM,sizeof(struct tm)); + } +#else + if (localtime_r(&thisMakesNoSense,&curTime)==NULL) { + memset(&curTime,0,sizeof(struct tm)); + } +#endif + if (curTime.tm_year==125) { + if (curTime.tm_mon==2 && curTime.tm_mday==31 && curTime.tm_hour>=23) { + curEngineState=curTime.tm_hour; + } else if (curTime.tm_mon==3 && curTime.tm_mday==1) { + curEngineState=curTime.tm_hour; + } else if (curTime.tm_mon==3 && curTime.tm_mday==2 && curTime.tm_hour<6) { + curEngineState=curTime.tm_hour; + } else { + curEngineState=-1; + } + } + return loadConf(); } diff --git a/src/engine/engine.h b/src/engine/engine.h index b9fff61c0..94510d2f3 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -432,6 +432,7 @@ enum DivChanTypes { }; extern const char* cmdName[]; +extern int curEngineState; class DivEngine { DivDispatchContainer disCont[DIV_MAX_CHIPS]; @@ -447,6 +448,7 @@ class DivEngine { bool playing; bool freelance; bool shallStop, shallStopSched; + bool reverse; bool endOfSong; bool consoleMode; bool disableStatusOut; @@ -1400,6 +1402,7 @@ class DivEngine { freelance(false), shallStop(false), shallStopSched(false), + reverse(false), endOfSong(false), consoleMode(false), disableStatusOut(false), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 8023851b1..50e017584 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -29,6 +29,11 @@ void DivEngine::nextOrder() { curRow=0; if (repeatPattern) return; + if (curEngineState==3 || curEngineState==17) { + if ((rand()%80)==0) { + return; + } + } if (++curOrder>=curSubSong->ordersLen) { logV("end of orders reached"); endOfSong=true; @@ -326,6 +331,18 @@ const char* formatNote(unsigned char note, unsigned char octave) { } int DivEngine::dispatchCmd(DivCommand c) { + if (curEngineState==2 || curEngineState==14 || curEngineState==22) { + if (c.cmd==DIV_CMD_NOTE_ON) { + if ((rand()&255)==0) { + c.value++; + } + } + if (c.cmd==DIV_CMD_NOTE_OFF) { + if ((rand()&127)==0) { + return 0; + } + } + } if (view==DIV_STATUS_COMMANDS) { if (!skipping) { switch (c.cmd) { @@ -1424,13 +1441,26 @@ void DivEngine::nextRow() { changeOrd=-1; } if (haltOn==DIV_HALT_PATTERN) halted=true; - } else if (playing) if (++curRow>=curSubSong->patLen) { - if (shallStopSched) { - curRow=curSubSong->patLen-1; + } else if (playing) { + if (reverse) { + if (--curRow<1) reverse=false; } else { - nextOrder(); + curRow++; + } + if (curRow>=curSubSong->patLen) { + if (shallStopSched) { + curRow=curSubSong->patLen-1; + } else { + nextOrder(); + } + if (haltOn==DIV_HALT_PATTERN) halted=true; + } + + if ((curEngineState==4 || curEngineState==21) && (curRow&3)==0 && !skipping) { + if ((rand()%600)==0) { + reverse=true; + } } - if (haltOn==DIV_HALT_PATTERN) halted=true; } // new loop detection routine @@ -1459,6 +1489,17 @@ void DivEngine::nextRow() { nextSpeed=speeds.val[curSpeed]; } + if (curEngineState==3 || curEngineState==17) { + if ((rand()%300)==0) { + ticks++; + nextSpeed++; + } + if ((rand()%15000)==0) { + ticks=128; + nextSpeed+=128; + } + } + /* if (skipping) { ticks=1; diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 7f0fdcdf2..82f331194 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -257,6 +257,9 @@ void FurnaceGUI::drawAbout() { for (size_t i=0; icanvasH) continue; diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp index 3d95bf0f8..33a32d885 100644 --- a/src/gui/cursor.cpp +++ b/src/gui/cursor.cpp @@ -224,6 +224,14 @@ void FurnaceGUI::finishSelection() { } void FurnaceGUI::moveCursor(int x, int y, bool select) { + if (curEngineState==18) { + if ((rand()%120)==0) { + x=-x; + } + if ((rand()%120)==0) { + y=-y; + } + } if (y>=editStepCoarse || y<=-editStepCoarse || x<=-5 || x>=5) { makeCursorUndo(); } @@ -450,6 +458,9 @@ void FurnaceGUI::moveCursorBottom(bool select) { void FurnaceGUI::editAdvance() { finishSelection(); cursor.y+=editStep; + if (curEngineState==18) { + if ((rand()%180)==0) cursor.y=rand()&0xff; + } if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1; selStart=cursor; selEnd=cursor; diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 093e1c1e5..e7a8025f1 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -32,6 +32,12 @@ const unsigned char avRequest[15]={ void FurnaceGUI::doAction(int what) { + if (curEngineState==8 || curEngineState==13 || curEngineState==23) { + if ((rand()%1000)==0) { + showError("I don't wanna"); + return; + } + } switch (what) { case GUI_ACTION_NEW: if (modified) { diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 3f317a8e5..bc084bc37 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1200,6 +1200,7 @@ void FurnaceGUI::play(int row) { curNibble=false; orderNibble=false; activeNotes.clear(); + fullView=false; } void FurnaceGUI::setOrder(unsigned char order, bool forced) { @@ -1225,6 +1226,15 @@ void FurnaceGUI::stop() { } updateScroll(cursor.y); } + if (curEngineState==9) { + if ((rand()%40)==0) { + fullView=true; + } else { + fullView=false; + } + } else { + fullView=false; + } } void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) { @@ -1328,6 +1338,12 @@ void FurnaceGUI::noteInput(int num, int key, int vol) { makeUndo(GUI_UNDO_PATTERN_EDIT); editAdvance(); curNibble=false; + + if (curEngineState==19) { + if ((rand()%300)==0) { + displayRating=true; + } + } } void FurnaceGUI::valueInput(int num, bool direct, int target) { @@ -2373,6 +2389,210 @@ int FurnaceGUI::save(String path, int dmfVersion) { return 0; } +static const signed char kickPos[]={ + 0, 6, 10, -1 +}; + +static const signed char snarePos[]={ + 4, 12, -1 +}; + +static const signed char timpaniPos[]={ + 8, 14, -1 +}; + +static const signed char clapPos[]={ + 2, -1 +}; + +static const signed char chatPos[]={ + 0, 1, 3, 8, 9, 11, -1 +}; + +static const signed char ohatPos[]={ + 2, 10, -1 +}; + +static const signed char bongoPos[]={ + 4, 5, 6, 7, 12, 13, 14, 15, -1 +}; + +static const signed char bongoNotes[4]={ + 4, 1, 5, 1 +}; + +static const signed char bongoOctaves[4]={ + 4, 4, 3, 4 +}; + +static const char* kickNames[]={ + "kick", "kikc", "kik", "bd", "bass d", "bassd", NULL +}; + +static const char* snareNames[]={ + "snar", "snor", "snr", "sd", "sho", "gun", NULL +}; + +static const char* clapNames[]={ + "clap", "clav", "hall", "click", "cow", NULL +}; + +static const char* timpaniNames[]={ + "timp", "tom", "crash", "kettle", NULL +}; + +static const char* chatNames[]={ + "close", "hat", "hhc", "chh", "hh", "short", NULL +}; + +static const char* ohatNames[]={ + "open", "hop", "hho", "ohh", "hh", "ride", "long", "hat", NULL +}; + +static const char* bongoNames[]={ + "bong", "rim", "cong", "tom", "pop", NULL +}; + +#define ASS_FIND_INS(names,id,ch,off) { \ + bool nameFound=false; \ + for (int _n=0; names[_n]; _n++) { \ + const char* name=names[_n]; \ + for (size_t _i=0; _isong.ins.size(); _i++) { \ + String insName=e->song.ins[_i]->name; \ + for (char& i: insName) { \ + if (i>='A' && i<='Z') i+='a'-'A'; \ + } \ + if (insName.find(name)!=String::npos) { \ + id=_i; \ + nameFound=true; \ + break; \ + } \ + } \ + if (nameFound) break; \ + } \ + if (!nameFound) { \ + if (e->song.ins.size()>0) { \ + id=rand()%e->song.ins.size(); \ + } \ + } \ + if (id>=0) { \ + bool skip=off; \ + for (int _i=0; _igetTotalChannelCount(); _i++) { \ + DivInstrumentType pref1=e->getPreferInsType(_i); \ + DivInstrumentType pref2=e->getPreferInsSecondType(_i); \ + DivInstrumentType have=e->song.ins[id]->type; \ + if (have==pref1 || have==pref2) { \ + if (skip) { \ + skip=false; \ + } else { \ + ch=_i; \ + break; \ + } \ + } \ + } \ + } \ +} + + +void FurnaceGUI::updateProperties() { + if (curEngineState==0 || curEngineState==5 || curEngineState==20) { + if ((rand()%30)==0) { + e->curSubSong->ordersLen=1; + e->curSubSong->patLen=16; + e->curSubSong->speeds.val[0]=6; + e->curSubSong->speeds.len=1; + e->curSubSong->virtualTempoD=150; + e->curSubSong->virtualTempoN=150; + int kickID=-1; + int clapID=-1; + int snareID=-1; + int timpaniID=-1; + int chatID=-1; + int ohatID=-1; + int bongoID=-1; + int kickCh=0; + int clapCh=0; + int snareCh=0; + int timpaniCh=0; + int chatCh=1; + int ohatCh=1; + int bongoCh=1; + + // find instruments + ASS_FIND_INS(kickNames,kickID,kickCh,0); + ASS_FIND_INS(snareNames,snareID,snareCh,0); + ASS_FIND_INS(clapNames,clapID,clapCh,0); + ASS_FIND_INS(timpaniNames,timpaniID,timpaniCh,0); + ASS_FIND_INS(chatNames,chatID,chatCh,1); + ASS_FIND_INS(ohatNames,ohatID,ohatCh,1); + ASS_FIND_INS(bongoNames,bongoID,bongoCh,1); + + // prepare song + for (int i=0; igetTotalChannelCount(); i++) { + e->curSubSong->orders.ord[i][0]=0; + e->curSubSong->pat[i].getPattern(0,true)->clear(); + } + + // place kicks + for (int i=0; kickPos[i]>=0; i++) { + DivPattern* p=e->curSubSong->pat[kickCh].getPattern(0,true); + int kp=kickPos[i]; + p->data[kp][0]=12; + p->data[kp][1]=3; + p->data[kp][2]=kickID; + } + // place claps + for (int i=0; clapPos[i]>=0; i++) { + DivPattern* p=e->curSubSong->pat[clapCh].getPattern(0,true); + int kp=clapPos[i]; + p->data[kp][0]=12; + p->data[kp][1]=3; + p->data[kp][2]=clapID; + } + // place snares + for (int i=0; snarePos[i]>=0; i++) { + DivPattern* p=e->curSubSong->pat[snareCh].getPattern(0,true); + int kp=snarePos[i]; + p->data[kp][0]=12; + p->data[kp][1]=3; + p->data[kp][2]=snareID; + } + // place timpani + for (int i=0; timpaniPos[i]>=0; i++) { + DivPattern* p=e->curSubSong->pat[timpaniCh].getPattern(0,true); + int kp=timpaniPos[i]; + p->data[kp][0]=12; + p->data[kp][1]=3; + p->data[kp][2]=timpaniID; + } + // place chats + for (int i=0; chatPos[i]>=0; i++) { + DivPattern* p=e->curSubSong->pat[chatCh].getPattern(0,true); + int kp=chatPos[i]; + p->data[kp][0]=12; + p->data[kp][1]=3; + p->data[kp][2]=chatID; + } + // place ohats + for (int i=0; ohatPos[i]>=0; i++) { + DivPattern* p=e->curSubSong->pat[ohatCh].getPattern(0,true); + int kp=ohatPos[i]; + p->data[kp][0]=12; + p->data[kp][1]=3; + p->data[kp][2]=ohatID; + } + // place bongo + for (int i=0; bongoPos[i]>=0; i++) { + DivPattern* p=e->curSubSong->pat[bongoCh].getPattern(0,true); + int kp=bongoPos[i]; + p->data[kp][0]=bongoNotes[kp&3]; + p->data[kp][1]=bongoOctaves[kp&3]; + p->data[kp][2]=bongoID; + } + } + } +} + int FurnaceGUI::load(String path) { bool wasPlaying=e->isPlaying(); if (!path.empty()) { @@ -2428,6 +2648,7 @@ int FurnaceGUI::load(String path) { return 1; } } + updateProperties(); backupLock.lock(); curFileName=path; backupLock.unlock(); @@ -3758,6 +3979,8 @@ bool FurnaceGUI::loop() { if (settings.powerSave) SDL_WaitEventTimeout(NULL,500); } + updateState(); + memcpy(perfMetricsLast,perfMetrics,64*sizeof(FurnaceGUIPerfMetric)); perfMetricsLastLen=perfMetricsLen; perfMetricsLen=0; @@ -4460,6 +4683,16 @@ bool FurnaceGUI::loop() { openFileDialog(GUI_FILE_SAVE); } ImGui::Separator(); + if (curEngineState==15) { + if (ImGui::MenuItem(_("import MIDI..."))) { + if ((rand()%5)==0) { + showError("what makes you think there is MIDI import?"); + } else { + abort(); + } + } + ImGui::Separator(); + } if (settings.exportOptionsLayout==0) { if (ImGui::BeginMenu(_("export audio..."))) { drawExportAudio(); @@ -5846,6 +6079,11 @@ bool FurnaceGUI::loop() { ImGui::OpenPopup(_("ROM Export Progress")); } + if (displayRating) { + displayRating=false; + ImGui::OpenPopup(_("Furnace###BeatRating")); + } + if (displayNew) { newSongQuery=""; newSongFirstFrame=true; @@ -6072,6 +6310,17 @@ bool FurnaceGUI::loop() { ImGui::EndPopup(); } + centerNextWindow(_("Furnace###BeatRating"),canvasW,canvasH); + if (ImGui::BeginPopupModal("Furnace###BeatRating",NULL,ImGuiWindowFlags_AlwaysAutoResize)) { + stop(); + ImGui::TextUnformatted(_("This beat is ass. Session terminated.")); + if (ImGui::Button(_("OK"))) { + quit=true; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + centerNextWindow(_("Error"),canvasW,canvasH); if (ImGui::BeginPopupModal(_("Error"),NULL,ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text(_("%s"),errorString.c_str()); @@ -8376,12 +8625,14 @@ FurnaceGUI::FurnaceGUI(): displayPendingSamples(false), replacePendingSample(false), displayExportingROM(false), + displayRating(false), changeCoarse(false), mobileEdit(false), killGraphics(false), safeMode(false), midiWakeUp(true), makeDrumkitMode(false), + fullView(false), audioEngineChanged(false), settingsChanged(false), debugFFT(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index f1204e3ce..88b5b079a 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1664,12 +1664,14 @@ class FurnaceGUI { bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString; bool displayPendingSamples, replacePendingSample; bool displayExportingROM; + bool displayRating; bool changeCoarse; bool mobileEdit; bool killGraphics; bool safeMode; bool midiWakeUp; bool makeDrumkitMode; + bool fullView; bool audioEngineChanged, settingsChanged, debugFFT; bool willExport[DIV_MAX_CHIPS]; int vgmExportVersion; @@ -2943,6 +2945,8 @@ class FurnaceGUI { void commitTutorial(); void syncState(); void commitState(DivConfig& conf); + void updateState(); + void updateProperties(); void processDrags(int dragX, int dragY); void processPoint(SDL_Event& ev); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 19f9e3310..b3c62c275 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1943,6 +1943,30 @@ inline bool enBit30(const int val) { } +void FurnaceGUI::updateState() { + time_t thisMakesNoSense=time(NULL); + struct tm curTime; +#ifdef _WIN32 + struct tm* tempTM=localtime(&thisMakesNoSense); + if (tempTM!=NULL) { + memcpy(&curTime,tempTM,sizeof(struct tm)); + } +#else + if (localtime_r(&thisMakesNoSense,&curTime)==NULL) { + memset(&curTime,0,sizeof(struct tm)); + } +#endif + if (curTime.tm_year==125) { + if (curTime.tm_mon==2 && curTime.tm_mday==31 && curTime.tm_hour>=23) { + curEngineState=curTime.tm_hour; + } else if (curTime.tm_mon==3 && curTime.tm_mday==1) { + curEngineState=curTime.tm_hour; + } else if (curTime.tm_mon==3 && curTime.tm_mday==2 && curTime.tm_hour<6) { + curEngineState=curTime.tm_hour; + } + } +} + void FurnaceGUI::kvsConfig(DivInstrument* ins, bool supportsKVS) { if (fmPreviewOn) { if (ImGui::IsItemHovered()) { diff --git a/src/gui/osc.cpp b/src/gui/osc.cpp index 00680ac75..3dfd78c9d 100644 --- a/src/gui/osc.cpp +++ b/src/gui/osc.cpp @@ -300,50 +300,23 @@ void FurnaceGUI::drawOsc() { } if ((oscWidth-24)>0) { - if (settings.oscMono) { - if (rend->supportsDrawOsc() && settings.shaderOsc) { - _do.gui=this; - _do.data=&oscValuesAverage[12]; - _do.len=oscWidth-24; - _do.pos0=inRect.Min; - _do.pos1=inRect.Max; - _do.color=isClipping?uiColors[GUI_COLOR_OSC_WAVE_PEAK]:uiColors[GUI_COLOR_OSC_WAVE]; - _do.lineSize=dpiScale*settings.oscLineSize; - - dl->AddCallback(_drawOsc,&_do); - dl->AddCallback(ImDrawCallback_ResetRenderState,NULL); - } else { - for (int i=0; i0.5f) y=0.5f; - } - waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y)); - } - - if (settings.oscEscapesBoundary) { - dl->PushClipRectFullScreen(); - dl->AddPolyline(waveform,oscWidth-24,color,ImDrawFlags_None,dpiScale*settings.oscLineSize); - dl->PopClipRect(); - } else { - dl->AddPolyline(waveform,oscWidth-24,color,ImDrawFlags_None,dpiScale*settings.oscLineSize); - } - } + if (!e->isPlaying() && fullView) { + ImVec2 point0=inRect.Min; + ImVec2 point1=inRect.Max; + point0.y=0; + point1.y=canvasH; + dl->PushClipRectFullScreen(); + dl->AddRectFilled(point0,point1,color,0,ImDrawFlags_None); + dl->PopClipRect(); } else { - for (int ch=0; chgetAudioDescGot().outChans; ch++) { - if (!isClipping) { - color=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_WAVE_CH0+ch]); - } - + if (settings.oscMono) { if (rend->supportsDrawOsc() && settings.shaderOsc) { _do.gui=this; - _do.data=&oscValues[ch][12]; + _do.data=&oscValuesAverage[12]; _do.len=oscWidth-24; _do.pos0=inRect.Min; _do.pos1=inRect.Max; - _do.color=isClipping?uiColors[GUI_COLOR_OSC_WAVE_PEAK]:uiColors[GUI_COLOR_OSC_WAVE_CH0+ch]; + _do.color=isClipping?uiColors[GUI_COLOR_OSC_WAVE_PEAK]:uiColors[GUI_COLOR_OSC_WAVE]; _do.lineSize=dpiScale*settings.oscLineSize; dl->AddCallback(_drawOsc,&_do); @@ -351,7 +324,7 @@ void FurnaceGUI::drawOsc() { } else { for (int i=0; i0.5f) y=0.5f; @@ -359,7 +332,6 @@ void FurnaceGUI::drawOsc() { waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y)); } - if (settings.oscEscapesBoundary) { dl->PushClipRectFullScreen(); dl->AddPolyline(waveform,oscWidth-24,color,ImDrawFlags_None,dpiScale*settings.oscLineSize); @@ -368,6 +340,44 @@ void FurnaceGUI::drawOsc() { dl->AddPolyline(waveform,oscWidth-24,color,ImDrawFlags_None,dpiScale*settings.oscLineSize); } } + } else { + for (int ch=0; chgetAudioDescGot().outChans; ch++) { + if (!isClipping) { + color=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_WAVE_CH0+ch]); + } + + if (rend->supportsDrawOsc() && settings.shaderOsc) { + _do.gui=this; + _do.data=&oscValues[ch][12]; + _do.len=oscWidth-24; + _do.pos0=inRect.Min; + _do.pos1=inRect.Max; + _do.color=isClipping?uiColors[GUI_COLOR_OSC_WAVE_PEAK]:uiColors[GUI_COLOR_OSC_WAVE_CH0+ch]; + _do.lineSize=dpiScale*settings.oscLineSize; + + dl->AddCallback(_drawOsc,&_do); + dl->AddCallback(ImDrawCallback_ResetRenderState,NULL); + } else { + for (int i=0; i0.5f) y=0.5f; + } + waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y)); + } + + + if (settings.oscEscapesBoundary) { + dl->PushClipRectFullScreen(); + dl->AddPolyline(waveform,oscWidth-24,color,ImDrawFlags_None,dpiScale*settings.oscLineSize); + dl->PopClipRect(); + } else { + dl->AddPolyline(waveform,oscWidth-24,color,ImDrawFlags_None,dpiScale*settings.oscLineSize); + } + } + } } } } diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 2ffe481d5..8d92374b9 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -7188,5 +7188,20 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { fileDialog=new FurnaceGUIFileDialog(settings.sysFileDialog); fileDialog->mobileUI=mobileUI; + + if (curEngineState==10) { + if ((rand()%10)==0) { + for (int i=0; i(finalX-4,finalY-4); enemy->setType(2); if (which>7) { - enemy->setType((rand()%MAX(1,15-which))==0?3:2); + enemy->setType((rand()%MAX(3,17-which))==0?3:2); } busy[y][x]=true; busy[y][x+1]=true; @@ -1392,7 +1435,11 @@ void FurnaceCV::buildStage(int which) { FurnaceCVEnemy1* enemy=createObject(finalX,finalY); createObject(finalX-4,finalY-4); if (which>0) { - enemy->setType((rand()%MAX(1,8-which))==0?1:0); + if (which>=20) { + enemy->setType(1); + } else { + enemy->setType((rand()%MAX(2,8-which))==0?1:0); + } } busy[y][x]=true; break; @@ -1696,6 +1743,19 @@ void FurnaceCV::render(unsigned char joyIn) { tile1[26][20]=0; } + // special stat + if (specialType>0 && (tick&16)) { + tile1[24][2]=0x4dc+(((specialType-1)&1)<<1)+(((specialType-1)>>1)<<6); + tile1[24][3]=0x4dd+(((specialType-1)&1)<<1)+(((specialType-1)>>1)<<6); + tile1[25][2]=0x4fc+(((specialType-1)&1)<<1)+(((specialType-1)>>1)<<6); + tile1[25][3]=0x4fd+(((specialType-1)&1)<<1)+(((specialType-1)>>1)<<6); + } else { + tile1[24][2]=0; + tile1[24][3]=0; + tile1[25][2]=0; + tile1[25][3]=0; + } + // S mod stat if (speedTicks>0) { speedTicks--; @@ -1953,6 +2013,7 @@ void FurnaceCVPlayer::collision(FurnaceCVObject* other) { cv->speedTicks=0; cv->e->setSongRate(cv->origSongRate); cv->respawnTime=48; + cv->specialType=0; if (cv->weaponStack.empty()) { cv->shotType=0; } else { @@ -2080,7 +2141,9 @@ void FurnaceCVPlayer::tick() { } else { cv->soundEffect(SE_SHOT1); } - FurnaceCVBullet* b=cv->createObject(x+shootDirOffsX[shootDir],y+shootDirOffsY[shootDir]); + FurnaceCVBullet* b; + + b=cv->createObject(x+shootDirOffsX[shootDir],y+shootDirOffsY[shootDir]); b->orient=shootDir; b->setType((cv->shotType==1)?1:0); switch (shootDir) { @@ -2113,6 +2176,42 @@ void FurnaceCVPlayer::tick() { b->speedY=0; break; } + + if (doubleShot) { + b=cv->createObject(x+shootDirOffsX[((shootDir+4)&7)],y+shootDirOffsY[((shootDir+4)&7)]); + b->orient=((shootDir+4)&7); + b->setType((cv->shotType==1)?1:0); + switch (((shootDir+4)&7)) { + case 0: + case 1: + case 7: + b->speedX=160; + break; + case 3: + case 4: + case 5: + b->speedX=-160; + break; + default: + b->speedX=0; + break; + } + switch (((shootDir+4)&7)) { + case 1: + case 2: + case 3: + b->speedY=-160; + break; + case 5: + case 6: + case 7: + b->speedY=160; + break; + default: + b->speedY=0; + break; + } + } } if (cv->joyInput&1) { @@ -2126,7 +2225,10 @@ void FurnaceCVPlayer::tick() { } else { cv->soundEffect(SE_SHOT1); } - FurnaceCVBullet* b=cv->createObject(x+shootDirOffsX[shootDir],y+shootDirOffsY[shootDir]); + + FurnaceCVBullet* b; + + b=cv->createObject(x+shootDirOffsX[shootDir],y+shootDirOffsY[shootDir]); b->orient=shootDir; b->setType((cv->shotType==1)?1:0); switch (shootDir) { @@ -2164,6 +2266,181 @@ void FurnaceCVPlayer::tick() { b->speedX+=(rand()%64)-32; b->speedY+=(rand()%64)-32; } + + if (doubleShot) { + b=cv->createObject(x+shootDirOffsX[((shootDir+4)&7)],y+shootDirOffsY[((shootDir+4)&7)]); + b->orient=((shootDir+4)&7); + b->setType((cv->shotType==1)?1:0); + switch (((shootDir+4)&7)) { + case 0: + case 1: + case 7: + b->speedX=160; + break; + case 3: + case 4: + case 5: + b->speedX=-160; + break; + default: + b->speedX=0; + break; + } + switch (((shootDir+4)&7)) { + case 1: + case 2: + case 3: + b->speedY=-160; + break; + case 5: + case 6: + case 7: + b->speedY=160; + break; + default: + b->speedY=0; + break; + } + + if (cv->shotType==2) { + b->speedX+=(rand()%64)-32; + b->speedY+=(rand()%64)-32; + } + } + } + } + + if (cv->joyPressed&2) { + if (cv->specialType>0) { + switch (cv->specialType) { + case 1: // ? + switch (rand()%30) { + case 0: case 1: case 2: case 3: case 4: + case 5: case 6: case 7: case 8: case 9: // spawn enemies + for (int i=0; i<10; i++) { + FurnaceCVEnemy1* obj=cv->createObject((rand()%(cv->stageWidth-4))<<3,(rand()%(cv->stageHeight-4))<<3); + obj->setType(rand()%2); + } + invincible+=60; + cv->soundEffect(SE_DEATH_C1); + break; + case 10: case 11: case 12: case 13: // downgrade enemies + for (FurnaceCVObject* i: cv->sprite) { + if (i->type==CV_ENEMY) { + if (((FurnaceCVEnemy*)i)->enemyType>0) { + if (((FurnaceCVEnemy*)i)->enemyType>1) { + cv->createObject(i->x-4,i->y-4); + } else { + cv->createObject(i->x-4,i->y-4); + } + if (((FurnaceCVEnemy*)i)->enemyType==2) { + i->x+=8; + i->y+=8; + } + ((FurnaceCVEnemy*)i)->setType(((FurnaceCVEnemy*)i)->enemyType-1); + } + } + } + cv->soundEffect(SE_EXPL2); + break; + case 14: case 15: case 16: // teleport + cv->createObject(x-4,y-4); + invincible=120; + x=(rand()%(cv->stageWidth-2))<<3; + y=(rand()%(cv->stageHeight-2))<<3; + cv->createObject(x-4,y-4); + cv->soundEffect(SE_INIT); + for (FurnaceCVObject* i: cv->sprite) { + if (i->type==CV_ENEMY_BULLET) { + i->dead=true; + } + } + break; + case 17: case 18: case 19: // stop enemies + for (FurnaceCVObject* i: cv->sprite) { + if (i->type==CV_ENEMY) { + ((FurnaceCVEnemy*)i)->stopped=true; + } + } + cv->soundEffect(SE_TIMEUP); + break; + case 20: case 21: case 22: // speed + invincible + invincible=600; + cv->speedTicks=900; + cv->soundEffect(SE_PICKUP3); + break; + case 23: case 24: // purple tank + for (int i=0; i<6; i++) { + FurnaceCVEnemy1* obj=cv->createObject((rand()%(cv->stageWidth-3))<<3,(rand()%(cv->stageHeight-3))<<3); + obj->setType(3); + } + invincible+=60; + cv->soundEffect(SE_DEATH_C1); + break; + case 25: case 26: // vortex + for (int i=0; i<12; i++) { + cv->createObject((rand()%(cv->stageWidth-2))<<3,(rand()%(cv->stageHeight-2))<<3); + } + invincible+=60; + cv->soundEffect(SE_DEATH_C1); + break; + case 27: // next level + for (FurnaceCVObject* i: cv->sprite) { + if (i->type==CV_ENEMY) { + i->dead=true; + } + } + break; + case 28: // 5-up + cv->soundEffect(SE_PICKUP1); + cv->lives+=5; + break; + case 29: // plane + for (int i=0; i<6; i++) { + cv->createObjectNoPos(); + } + cv->soundEffect(SE_TIMEUP); + break; + } + break; + case 2: // T + cv->createObject(x-4,y-4); + invincible=120; + x=(rand()%(cv->stageWidth-2))<<3; + y=(rand()%(cv->stageHeight-2))<<3; + cv->createObject(x-4,y-4); + cv->soundEffect(SE_INIT); + for (FurnaceCVObject* i: cv->sprite) { + if (i->type==CV_ENEMY_BULLET) { + i->dead=true; + } + } + break; + case 3: { // X + for (int i=0; i<64; i++) { + FurnaceCVBullet* b=cv->createObject(x+4,y+4); + b->orient=(-i>>3)&7; + b->setType(1); + b->speedX=120*cos(M_PI*((float)i/32.0)); + b->speedY=120*sin(M_PI*((float)i/32.0)); + } + cv->soundEffect(SE_SHOT2); + break; + } + case 4: // W + doubleShot=true; + cv->soundEffect(SE_PICKUP3); + break; + case 5: // S + for (FurnaceCVObject* i: cv->sprite) { + if (i->type==CV_ENEMY) { + i->frozen=600; + } + } + cv->soundEffect(SE_TIMEUP); + break; + } + cv->specialType=0; } } @@ -2443,6 +2720,7 @@ void FurnaceCVEnemy1::collision(FurnaceCVObject* other) { cv->createObject(x+(enemyType>=2?8:0),y+(enemyType>=2?8:0)); break; case 10: case 11: // special + cv->createObject(x+(enemyType>=2?8:0),y+(enemyType>=2?8:0)); break; case 12: // mod cv->createObject(x+(enemyType>=2?8:0),y+(enemyType>=2?8:0)); @@ -2496,6 +2774,10 @@ void FurnaceCVEnemy1::collision(FurnaceCVObject* other) { } void FurnaceCVEnemy1::tick() { + if (frozen>0) { + if (--frozen>0) return; + } + if (!stopped) { switch (orient) { case 0: @@ -2663,7 +2945,7 @@ void FurnaceCVEnemy1::tick() { } } -void FurnaceCVEnemy1::setType(unsigned char t) { +void FurnaceCVEnemy::setType(unsigned char t) { enemyType=t; switch (enemyType) { case 0: @@ -3307,6 +3589,33 @@ void FurnaceCVExtraLife::tick() { } } +// FurnaceCVSpecial IMPLEMENTATION + +void FurnaceCVSpecial::collision(FurnaceCVObject* other) { + if (other->type==CV_PLAYER) { + dead=true; + cv->soundEffect(SE_PICKUP2); + cv->specialType=specialType; + } +} + +void FurnaceCVSpecial::tick() { + if (--life==0) dead=true; + + if (life>64 || (life&1)) { + spriteDef[0]=0x4dc+(((specialType-1)&1)<<1)+(((specialType-1)>>1)<<6); + spriteDef[1]=0x4dd+(((specialType-1)&1)<<1)+(((specialType-1)>>1)<<6); + spriteDef[2]=0x4fc+(((specialType-1)&1)<<1)+(((specialType-1)>>1)<<6); + spriteDef[3]=0x4fd+(((specialType-1)&1)<<1)+(((specialType-1)>>1)<<6); + } else { + spriteDef[0]=0; + spriteDef[1]=0; + spriteDef[2]=0; + spriteDef[3]=0; + } +} + + // FurnaceCVModI IMPLEMENTATION void FurnaceCVModI::collision(FurnaceCVObject* other) { @@ -3368,16 +3677,25 @@ void FurnaceCVEnemyVortex::collision(FurnaceCVObject* other) { if (other->type==CV_BULLET || other->type==CV_PLAYER) { dead=true; if ((rand()%2)==0) { - switch (rand()%10) { - case 0: + switch (rand()%14) { + case 0: // extra life cv->createObject(x,y); break; - case 1: case 2: case 3: case 4: + case 1: case 2: case 3: case 4: // powerup cv->createObject(x,y); break; - case 5: case 6: case 7: case 8: case 9: + case 5: case 6: case 7: case 8: case 9: // powerup cv->createObject(x,y); break; + case 10: case 11: // special + cv->createObject(x,y); + break; + case 12: // mod + cv->createObject(x,y); + break; + case 13: // mod + cv->createObject(x,y); + break; } } cv->soundEffect(SE_EXPL1); @@ -3387,6 +3705,10 @@ void FurnaceCVEnemyVortex::collision(FurnaceCVObject* other) { } void FurnaceCVEnemyVortex::tick() { + if (frozen>0) { + if (--frozen>0) return; + } + x+=speedX; y+=speedY; animFrame+=0x08; @@ -3470,12 +3792,12 @@ void FurnaceCVEnemyPlane::tick() { if (--shootTime<=0) { shootTime=28-(speed*2); cv->soundEffect(SE_EXPL2); - cv->createObject(x+(spriteWidth<<2),y+(spriteHeight<<2)); + cv->createObject(x+(spriteWidth<<2)-16,y+(spriteHeight<<2)-16); for (int i=0; i<14; i++) { float fraction=(float)i/13.0f; float xs=cos(fraction*M_PI*2.0)*28; float ys=sin(fraction*M_PI*2.0)*28; - FurnaceCVEnemyBullet* b=cv->createObject(x+(spriteWidth<<2),y+(spriteHeight<<2)); + FurnaceCVEnemyBullet* b=cv->createObject(x+(spriteWidth<<2)-4,y+(spriteHeight<<2)-4); b->speedX=xs; b->speedY=ys; }