aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2015-10-02 23:48:34 +0800
committerGravatar Chris Xiong <chirs241097@gmail.com> 2015-10-02 23:48:34 +0800
commitb9d4b60486e224261990c0732852513e86dd94ff (patch)
tree9f9dd20a888875833af11dc7a67b849a74825b5b
parentabffe5f72dfb39da8d480ce3c4b546404684a060 (diff)
downloadbullet-lab-remix-b9d4b60486e224261990c0732852513e86dd94ff.tar.xz
Add BLR3 files. Modify Readme.
Currently BLR3 is VERY primitive!!
-rw-r--r--Readme10
-rw-r--r--src/blr3.dtpbin0 -> 459577 bytes
-rw-r--r--src/core/bullet.cpp101
-rw-r--r--src/core/bullet.hpp49
-rw-r--r--src/core/corepublic.hpp26
-rw-r--r--src/core/coreshared.hpp5
-rw-r--r--src/core/fncmodules.cpp1
-rw-r--r--src/core/fncmodules.hpp1
-rw-r--r--src/core/fncwrapper.hpp1
-rw-r--r--src/core/gamescene.cpp70
-rw-r--r--src/core/laser.cpp1
-rw-r--r--src/core/player.cpp40
-rw-r--r--src/core/player.hpp21
-rw-r--r--src/core/scriptshared.hpp150
-rw-r--r--src/core/tower.cpp1
-rw-r--r--src/core/vmrunner.cpp280
-rw-r--r--src/core/vmrunner.hpp48
-rw-r--r--src/main.cpp30
-rw-r--r--src/makefile41
-rw-r--r--src/master/init.cpp1
-rw-r--r--src/master/intro.cpp202
-rw-r--r--src/master/master.hpp64
-rw-r--r--src/master/resources.cpp18
-rw-r--r--src/master/resources.hpp7
-rw-r--r--src/master/scenemgr.cpp63
-rw-r--r--src/ui/mainmenu.cpp69
-rw-r--r--src/ui/menuframework/menuclass.cpp115
-rw-r--r--src/ui/menuframework/menuframework.hpp56
-rw-r--r--src/ui/menus.hpp16
29 files changed, 1485 insertions, 2 deletions
diff --git a/Readme b/Readme
index 928a400..cd70627 100644
--- a/Readme
+++ b/Readme
@@ -1,7 +1,13 @@
This text is encoded in UTF-8.
-Now BLR3 is in active development. But I am not going to upload any source until
-a prototype comes out.
+This file is mainly about BulletLabRemix II. BulletLabRemix III is still under
+heavy development and can be found in the 'src' directory. The script virtual
+machine is still incomplete and is not enabled. The current version is extremely
+primitive and is only for testing/debugging purpose.
+
+There was a old testbed version with virtual machine enabled and 3D first-person
+view implemented. Please contact me or check chirs241097.xicp.net/BulletLabRemix
+/public/BLRScriptTestbed/ (likely to be offline) if you want to get a copy.
BulletLabRemix II readme
diff --git a/src/blr3.dtp b/src/blr3.dtp
new file mode 100644
index 0000000..1f62320
--- /dev/null
+++ b/src/blr3.dtp
Binary files differ
diff --git a/src/core/bullet.cpp b/src/core/bullet.cpp
new file mode 100644
index 0000000..175bae4
--- /dev/null
+++ b/src/core/bullet.cpp
@@ -0,0 +1,101 @@
+#include "bullet.hpp"
+#include "../master/resources.hpp"
+#include <cstdlib>
+const char* bsnames[]={"green_bullet","cyan_bullet","yellow_bullet","purple_bullet",
+ "red_bullet","white_bullet","blue_bullet","orange_bullet",
+ "grey_bullet","circle_bullet"};
+void bulletBase::init(...){exist=true;renderscale=1;}
+void bulletBase::update()
+{
+ if(!exist)return;
+ vel=vel+acc;
+ if(vel.l()>velim&&velim>0)vel=velim*vel.getNormalized();
+ pos=pos-vel;
+ if(!extborder&&(pos.x<-10||pos.y<-10||pos.x>810||pos.y>610))exist=false;
+ //check clr & collision
+}
+void bulletBase::render()
+{
+ //test view mode
+ bmInstance->getBulEntity2D(basecolor)->setColor(0xC0FFFFFF);
+ bmInstance->getBulEntity2D(basecolor)->render(pos.x,pos.y,0,renderscale*0.6);
+}
+bulletBase::~bulletBase(){}
+
+void bulletBonus::update()
+{
+ //the player is not implemented yet...
+}
+
+void bulletManager::init()
+{
+ alloced=used=0;
+ for(int i=0;i<(int)COLOR_COUNT;++i)
+ {
+ bulent2d[i]=new smEntity2D(ssanm.getTextureInfo(bsnames[i])->tex,ssanm.getTextureInfo(bsnames[i])->rect);
+ bulent3d[i]=new smEntity3D(ssanm.getTextureInfo(bsnames[i])->tex,ssanm.getTextureInfo(bsnames[i])->rect);
+ }
+}
+void bulletManager::deinit()
+{
+ if(alloced){for(int i=0;i<alloced;++i)delete bullets[i];alloced=used=0;}
+ for(int i=0;i<(int)COLOR_COUNT;++i)
+ {delete bulent2d[i];delete bulent3d[i];}
+}
+template<class T>
+int bulletManager::allocBullet()
+{
+ if(!alloced)
+ {
+ alloced=1;
+ bullets[0]=new T;
+ return 0;
+ }
+ else
+ {
+ int i;
+ for(i=0;i<alloced;++i)
+ if(!bullets[i]->exist)break;
+ if(i==alloced)
+ bullets[alloced++]=new T;
+ return i;
+ }
+ return -1;
+}
+template<class T>
+int bulletManager::createBullet()
+{
+ //stub...
+ int ptr=allocBullet<T>();
+ bullets[ptr]->init();
+ return ptr;
+}
+void bulletManager::updateBullet()
+{
+ static int b=0;
+ ++b;
+ if(b>15)
+ {
+ int a=createBullet<bulletBase>();
+ bulletBase* x=getHandle(a);
+ x->pos=smvec2d(400,300);
+ x->vel=smvec2d(rand()%100-50,rand()%100-50);
+ x->vel.normalize();
+ b=0;
+ }
+ for(int i=0;i<alloced;++i)
+ if(bullets[i]->exist)
+ bullets[i]->update();
+}
+void bulletManager::renderBullet()
+{
+ for(int i=0;i<alloced;++i)
+ if(bullets[i]->exist)
+ {
+ bullets[i]->render();
+ }
+}
+smEntity2D* bulletManager::getBulEntity2D(TColors col){return bulent2d[col];}
+smEntity3D* bulletManager::getBulEntity3D(TColors col){return bulent3d[col];}
+bulletBase* bulletManager::getHandle(int id){if(id<alloced)return bullets[id];else return NULL;}
+bulletManager *bmInstance;
diff --git a/src/core/bullet.hpp b/src/core/bullet.hpp
new file mode 100644
index 0000000..5f4fe2f
--- /dev/null
+++ b/src/core/bullet.hpp
@@ -0,0 +1,49 @@
+#ifndef BULLET_H
+#define BULLET_H
+#include "smmath.hpp"
+#include "smentity.hpp"
+#include "coreshared.hpp"
+class bulletBase
+{
+public:
+ smvec2d pos,vel,acc;
+ float velim;
+ //velim: velocity scalar limit.
+ float collrange,scollrange,renderscale;
+ //collision range and semi-collision range. Replacing "collable" and "scollable".
+ bool extborder,invincible;
+ //extborder: true=not removed if out of screen.
+ //invincible: true=not removed if collided with player or in range of CLR.
+ bool exist,addblend;
+ int attrd[8];
+ float attrf[8];
+ TColors basecolor;
+ virtual void init(...);
+ virtual void update();
+ virtual void render();
+ virtual ~bulletBase();
+};
+class bulletBonus:public bulletBase
+{
+ void update()override;
+};
+class bulletManager
+{
+private:
+ bulletBase* bullets[10240];
+ int alloced,used;
+ smEntity2D* bulent2d[COLOR_COUNT];
+ smEntity3D* bulent3d[COLOR_COUNT];
+public:
+ void init();
+ void deinit();
+ template<class T>int allocBullet();
+ template<class T>int createBullet();
+ void updateBullet();
+ void renderBullet();
+ bulletBase* getHandle(int id);
+ smEntity2D* getBulEntity2D(TColors col);
+ smEntity3D* getBulEntity3D(TColors col);
+};
+extern bulletManager *bmInstance;
+#endif
diff --git a/src/core/corepublic.hpp b/src/core/corepublic.hpp
new file mode 100644
index 0000000..fb06073
--- /dev/null
+++ b/src/core/corepublic.hpp
@@ -0,0 +1,26 @@
+#ifndef COREPUBLIC_H
+#define COREPUBLIC_H
+#include "smelt.hpp"
+#include "smttfont.hpp"
+#include "bullet.hpp"
+#include "player.hpp"
+#include "../master/master.hpp"
+class gameScene:public scenePrototype
+{
+private:
+ bool tpmode;
+ static SMELT *sm;
+ SMTRG rtarget;
+ smQuad tgquad;
+ smTTFont ttfont;
+ float udly;
+ int utime;
+public:
+ gameScene();
+ ~gameScene();
+ bool sceneUpdate()override;
+ bool sceneRender()override;
+ bool threadUpdate()override;
+};
+extern gameScene* gameScn;
+#endif
diff --git a/src/core/coreshared.hpp b/src/core/coreshared.hpp
new file mode 100644
index 0000000..7c49afd
--- /dev/null
+++ b/src/core/coreshared.hpp
@@ -0,0 +1,5 @@
+#ifndef CORESHARED_H
+#define CORESHARED_H
+enum TColors
+{green=0,cyan,yellow,purple,red,white,blue,orange,grey,circle,COLOR_COUNT};
+#endif
diff --git a/src/core/fncmodules.cpp b/src/core/fncmodules.cpp
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/src/core/fncmodules.cpp
@@ -0,0 +1 @@
+
diff --git a/src/core/fncmodules.hpp b/src/core/fncmodules.hpp
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/src/core/fncmodules.hpp
@@ -0,0 +1 @@
+
diff --git a/src/core/fncwrapper.hpp b/src/core/fncwrapper.hpp
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/src/core/fncwrapper.hpp
@@ -0,0 +1 @@
+
diff --git a/src/core/gamescene.cpp b/src/core/gamescene.cpp
new file mode 100644
index 0000000..3bda53f
--- /dev/null
+++ b/src/core/gamescene.cpp
@@ -0,0 +1,70 @@
+#include "../master/master.hpp"
+#include "../master/resources.hpp"
+#include "corepublic.hpp"
+#include "bullet.hpp"
+#include "player.hpp"
+SMELT* gameScene::sm=NULL;
+gameScene::gameScene()
+{
+ sm=smGetInterface(SMELT_APILEVEL);
+ rtarget=sm->smTargetCreate(800,600);
+ tgquad.tex=sm->smTargetTexture(rtarget);
+ tgquad.blend=BLEND_ALPHABLEND;
+ tgquad.v[0].x=190;tgquad.v[0].y=75;
+ tgquad.v[1].x=950;tgquad.v[1].y=75;
+ tgquad.v[2].x=950;tgquad.v[2].y=645;
+ tgquad.v[3].x=190;tgquad.v[3].y=645;
+ for(int i=0;i<4;++i)tgquad.v[i].z=0.5,tgquad.v[i].col=0xFFFFFFFF;
+ int rw=sm->smTextureGetWidth(tgquad.tex);
+ int rh=sm->smTextureGetHeight(tgquad.tex);
+ tgquad.v[0].tx=0;tgquad.v[0].ty=0;
+ tgquad.v[1].tx=800./rw;tgquad.v[1].ty=0;
+ tgquad.v[2].tx=800./rw;tgquad.v[2].ty=600./rh;
+ tgquad.v[3].tx=0;tgquad.v[3].ty=600./rh;
+ bmInstance=new bulletManager;
+ player=new playerBase;
+ utime=0;
+ ttfont.loadTTFFromMemory(blrdtp.getFilePtr("FreeMono.ttf"),blrdtp.getFileSize("FreeMono.ttf"),12);
+ bmInstance->init();
+}
+gameScene::~gameScene()
+{
+ bmInstance->deinit();
+ delete bmInstance;
+ delete player;
+ bmInstance=NULL;
+ ttfont.releaseTTF();
+ sm->smTargetFree(rtarget);
+ sm->smRelease();
+}
+bool gameScene::sceneUpdate()
+{
+ sm->smRenderBegin2D(true,rtarget);
+ sm->smClrscr(0xFF000000);
+ bmInstance->renderBullet();
+ player->render();
+ sm->smRenderEnd();
+ return false;
+}
+bool gameScene::sceneRender()
+{
+ sm->smClrscr(0xFF000080);
+ sm->smRenderQuad(&tgquad);
+ udly+=sm->smGetDelta();
+ extern sceneManager *sceneMgr;float lps=sceneMgr->getLPS();
+ if(udly>1){udly=0;utime=sceneMgr->getTHUpdateTime();}
+ ttfont.updateString(L"LPS: %.2f",lps);
+ ttfont.render(0,680,0xFFFFFFFF,ALIGN_LEFT);
+ ttfont.updateString(L"Update Time: %dns",utime);
+ ttfont.render(0,695,0xFFFFFFFF,ALIGN_LEFT);
+ ttfont.updateString(L"FPS: %.2f",sm->smGetFPS());
+ ttfont.render(0,710,0xFFFFFFFF,ALIGN_LEFT);
+ return false;
+}
+bool gameScene::threadUpdate()
+{
+ bmInstance->updateBullet();
+ player->update();
+ return false;
+}
+gameScene* gameScn;
diff --git a/src/core/laser.cpp b/src/core/laser.cpp
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/src/core/laser.cpp
@@ -0,0 +1 @@
+
diff --git a/src/core/player.cpp b/src/core/player.cpp
new file mode 100644
index 0000000..3407a0c
--- /dev/null
+++ b/src/core/player.cpp
@@ -0,0 +1,40 @@
+#include "smelt.hpp"
+#include "player.hpp"
+#include "../master/resources.hpp"
+SMELT* playerBase::sm=NULL;
+playerBase::playerBase(float _x,float _y)
+{
+ sm=smGetInterface(SMELT_APILEVEL);
+ playerent=new smEntity2D(ssanm.getTextureInfo("player")->tex,ssanm.getTextureInfo("player")->rect);
+ playerent->setCentre(12,12);
+ pos.x=_x;pos.y=_y;
+ plyrctl[0]=SMK_UP;plyrctl[1]=SMK_DOWN;
+ plyrctl[2]=SMK_LEFT;plyrctl[3]=SMK_RIGHT;
+ plyrctl[4]=SMK_SHIFT;
+ rot=0;
+}
+playerBase::~playerBase()
+{
+ delete playerent;sm->smRelease();
+}
+void playerBase::update()
+{
+ //player control...
+ static float realspeed=0;
+ if(sm->smGetKeyState(plyrctl[4]))
+ realspeed=0.85;else realspeed=3.5;
+ if(sm->smGetKeyState(plyrctl[0]))
+ {if(pos.y>15)pos.y-=realspeed;else pos.y=15;}
+ if(sm->smGetKeyState(plyrctl[1]))
+ {if(pos.y<585)pos.y+=realspeed;else pos.y=585;}
+ if(sm->smGetKeyState(plyrctl[2]))
+ {if(pos.x>15)pos.x-=realspeed;else pos.x=15;}
+ if(sm->smGetKeyState(plyrctl[3]))
+ {if(pos.x<785)pos.x+=realspeed;else pos.x=785;}
+ rot+=17./1800.*PI;
+}
+void playerBase::render()
+{
+ playerent->render(pos.x,pos.y,rot,0.7);
+}
+playerBase* player;
diff --git a/src/core/player.hpp b/src/core/player.hpp
new file mode 100644
index 0000000..408c174
--- /dev/null
+++ b/src/core/player.hpp
@@ -0,0 +1,21 @@
+#ifndef PLAYER_H
+#define PLAYER_H
+#include "smmath.hpp"
+#include "smentity.hpp"
+class playerBase
+{
+private:
+ smEntity2D* playerent;
+ static SMELT* sm;
+public:
+ smvec2d pos;
+ float rot,vel,velp;
+ int plyrctl[6];
+
+ playerBase(float _x=400,float _y=400);
+ ~playerBase();
+ void update();
+ void render();
+};
+extern playerBase* player;
+#endif
diff --git a/src/core/scriptshared.hpp b/src/core/scriptshared.hpp
new file mode 100644
index 0000000..01d423a
--- /dev/null
+++ b/src/core/scriptshared.hpp
@@ -0,0 +1,150 @@
+#ifndef SCRIPTSHARED_H
+#define SCRIPTSHARED_H
+#include <cmath>
+#define eps 1e-6
+typedef union _Udata{//data union
+ int i;float r;unsigned long d;
+ _Udata(){d=0;}
+}Udata;
+typedef struct _Idata{//data union, with type tag and operators
+ _Udata D;
+ int type;//0=int, 1=float
+ _Idata(){D.d=0;type=0;}
+ _Idata(int _type,int _data)
+ {type=_type;if(type==0)D.i=_data;else D.r=(float)_data;}
+ float &r(){return D.r;}
+ int &i(){return D.i;}
+ unsigned long &d(){return D.d;}
+ _Idata operator =(_Idata r)
+ {
+ if(type==1&&r.type==0)this->r()=(float)r.i();
+ else if(type==0&&r.type==1)this->i()=(int)r.r();
+ else this->d()=r.d();
+ return *this;
+ }
+ _Idata operator +=(_Idata r)
+ {
+ if(type==1&&r.type==0)this->r()+=(float)r.i();
+ if(type==0&&r.type==1)this->i()+=(int)r.r();
+ if(type==0&&r.type==0)this->i()+=r.i();
+ if(type==1&&r.type==1)this->r()+=r.r();
+ return *this;
+ }
+ _Idata operator -=(_Idata r)
+ {
+ if(type==1&&r.type==0)this->r()-=(float)r.i();
+ if(type==0&&r.type==1)this->i()-=(int)r.r();
+ if(type==0&&r.type==0)this->i()-=r.i();
+ if(type==1&&r.type==1)this->r()-=r.r();
+ return *this;
+ }
+ _Idata operator *=(_Idata r)
+ {
+ if(type==1&&r.type==0)this->r()*=(float)r.i();
+ if(type==0&&r.type==1)this->i()*=(int)r.r();
+ if(type==0&&r.type==0)this->i()*=r.i();
+ if(type==1&&r.type==1)this->r()*=r.r();
+ return *this;
+ }
+ _Idata operator /=(_Idata r)
+ {
+ if(type==1&&r.type==0)this->r()/=(float)r.i();
+ if(type==0&&r.type==1)this->i()/=(int)r.r();
+ if(type==0&&r.type==0)this->i()/=r.i();
+ if(type==1&&r.type==1)this->r()/=r.r();
+ return *this;
+ }
+ _Idata operator %=(_Idata r)
+ {
+ if(type==1&&r.type==0)throw;
+ if(type==0&&r.type==1)throw;
+ if(type==0&&r.type==0)this->i()%=r.i();
+ if(type==1&&r.type==1)throw;
+ return *this;
+ }
+ _Idata operator &=(_Idata r)
+ {
+ if(type==1&&r.type==0)throw;
+ if(type==0&&r.type==1)throw;
+ if(type==0&&r.type==0)this->i()&=r.i();
+ if(type==1&&r.type==1)throw;
+ return *this;
+ }
+ _Idata operator |=(_Idata r)
+ {
+ if(type==1&&r.type==0)throw;
+ if(type==0&&r.type==1)throw;
+ if(type==0&&r.type==0)this->i()|=r.i();
+ if(type==1&&r.type==1)throw;
+ return *this;
+ }
+ _Idata operator ^=(_Idata r)
+ {
+ if(type==1&&r.type==0)throw;
+ if(type==0&&r.type==1)throw;
+ if(type==0&&r.type==0)this->i()^=r.i();
+ if(type==1&&r.type==1)throw;
+ return *this;
+ }
+ _Idata operator ~()
+ {
+ if(type==1)throw;
+ if(type==0)i()=~i();
+ return *this;
+ }
+ _Idata operator ++()
+ {
+ if(type==1)throw;
+ if(type==0)i()=i()+1;
+ return *this;
+ }
+ _Idata operator --()
+ {
+ if(type==1)throw;
+ if(type==0)i()=i()-1;
+ return *this;
+ }
+ bool ltz()
+ {
+ if(type==0)return i()<0;
+ if(type==1)return fabs(r())>eps&&r()<0;
+ throw;
+ }
+ bool elz()
+ {
+ if(type==0)return i()<=0;
+ if(type==1)return fabs(r())<eps||(fabs(r())>eps&&r()<0);
+ throw;
+ }
+ bool gtz()
+ {
+ if(type==0)return i()>0;
+ if(type==1)return fabs(r())>eps&&r()>0;
+ throw;
+ }
+ bool egz()
+ {
+ if(type==0)return i()>=0;
+ if(type==1)return fabs(r())<eps||(fabs(r())>eps&&r()>0);
+ throw;
+ }
+ bool eqz()
+ {
+ if(type==0)return i()==0;
+ if(type==1)return fabs(r())<eps;
+ throw;
+ }
+ bool nez()
+ {
+ if(type==0)return i()!=0;
+ if(type==1)return fabs(r())>eps;
+ throw;
+ }
+}Idata;
+typedef struct _SPara{Udata data;int type;char *fnc;}SPara;//parameters
+typedef struct _SInst//instructions
+{
+ int id;
+ SPara para1,para2;
+}SInst;
+#endif
diff --git a/src/core/tower.cpp b/src/core/tower.cpp
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/src/core/tower.cpp
@@ -0,0 +1 @@
+
diff --git a/src/core/vmrunner.cpp b/src/core/vmrunner.cpp
new file mode 100644
index 0000000..1942d74
--- /dev/null
+++ b/src/core/vmrunner.cpp
@@ -0,0 +1,280 @@
+#include "vmrunner.hpp"
+#include "coreshared.hpp"
+#include <cstdlib>
+unsigned getHash(const char *s)
+{
+ unsigned r=5381;char c;
+ while((c=(*s++)))r=((r<<5)+r)+c;
+ return r;
+}
+Idata& blrScriptVM::fetchData(SPara para,bool forcerw)
+{
+ static Idata cret;
+ switch (para.type)
+ {
+ case 0:
+ if(forcerw)throw;
+ cret.i()=para.data.i;cret.type=0;
+ return cret;
+ case 1:
+ if(forcerw)throw;
+ cret.r()=para.data.r;cret.type=1;
+ return cret;
+ case 2:
+ return ir[para.data.i];
+ case 3:
+ return rr[para.data.i];
+ case 4:
+ return ia[para.data.i];
+ case 5:
+ return ra[para.data.i];
+ case 6:
+ return ia[ir[para.data.i].i()];
+ case 7:
+ return ra[ir[para.data.i].i()];
+ default:
+ throw;
+ }
+}
+int blrScriptVM::mgetc()
+{
+ if(cbyte-sbyte<fsize)
+ {++cbyte;return (int)*(cbyte-1);}
+ else return -1;
+}
+void blrScriptVM::readPara(SPara *para)
+{
+ para->type=mgetc();
+ para->data.d=0;
+ int l=0;
+ switch(para->type)
+ {
+ case 1: l=8;break;
+ case 0: case 4: case 5: l=4;break;
+ case 2: case 3: case 6: case 7: l=1;break;
+ default: break;
+ }
+ for(int i=0;i<l;++i)
+ {
+ para->data.d<<=8LL;
+ para->data.d|=(unsigned long long)mgetc();
+ }
+}
+int blrScriptVM::loadLSBFromMemory(const char *ptr,DWORD size)
+{
+ cbyte=sbyte=ptr;fsize=size;
+ int ibyt=0,infunc=0;
+ while(~(ibyt=mgetc()))
+ {
+ ++ic;inst[ic].id=ibyt;
+ switch(inst[ic].id)
+ {
+ case 0xFF: case 0x02:
+ {
+ int nl=mgetc();
+ if(nl==-1)return 1;
+ if(inst[ic].id==0xFF){if(infunc)return 1;else infunc=1;}
+ if(inst[ic].id==0x02)if(!infunc)return 1;
+ inst[ic].para1.fnc=(char*)calloc(sizeof(char),nl+1);
+ inst[ic].para1.type=8;
+ for(int i=0;i<nl;++i)inst[ic].para1.fnc[i]=mgetc();
+ if(inst[ic].id==0xFF)
+ {
+ fncent[fncnt].pos=ic;
+ fncent[fncnt++].hash=getHash(inst[ic].para1.fnc);
+ }
+ }
+ break;
+ case 0x00: break;
+ case 0xFE:
+ if(!infunc)return 1;
+ infunc=0;
+ break;
+ case 0x0F:
+ if(!infunc)return 1;
+ break;
+ case 0x01: case 0x0C: case 0x0D:
+ case 0x0E: case 0x11: case 0x12:
+ case 0x13: case 0x14: case 0x15:
+ case 0x16: case 0x21:
+ if(!infunc)return 1;
+ readPara(&inst[ic].para1);
+ break;
+ case 0x03: case 0x04: case 0x05:
+ case 0x06: case 0x07: case 0x08:
+ case 0x09: case 0x0A: case 0x0B:
+ case 0x22: case 0x23:
+ if(!infunc)return 1;
+ readPara(&inst[ic].para1);
+ readPara(&inst[ic].para2);
+ break;
+ }
+ }
+ return 0;
+}
+int blrScriptVM::getInstCount(){return pinst;}
+void blrScriptVM::runFunction(const char *fncnym)
+{
+ int nymhash=getHash(fncnym),cur=0;pinst=0;
+ for(int i=0;i<fncnt;++i)if(nymhash==fncent[i].hash){cur=fncent[i].pos;break;}
+ while(inst[cur].id!=0xFE)
+ {
+ int jmp=0;++pinst;
+ //printf("@offset %d, instId 0x%x\n",cur,inst[cur].id);
+ rr[100].r()=0.01667;
+ //printf("r00: %f, r01: %f\n",rr[0].r(),rr[1].r());
+ switch(inst[cur].id)
+ {
+ case 0x00:
+ //no-op
+ break;
+ case 0xFF:
+ //no-op
+ break;
+ case 0x01:
+ callStk.push(fetchData(inst[cur].para1));
+ break;
+ case 0x02:
+ //callFnc(inst[cur].para1.fnc);
+ //callstk.clear();
+ //printf("stubbed call %s\n",inst[cur].para1.fnc);
+ break;
+ case 0x03:
+ fetchData(inst[cur].para1,true)=fetchData(inst[cur].para2);
+ break;
+ case 0x04:
+ fetchData(inst[cur].para1,true)+=fetchData(inst[cur].para2);
+ break;
+ case 0x05:
+ fetchData(inst[cur].para1,true)-=fetchData(inst[cur].para2);
+ break;
+ case 0x06:
+ fetchData(inst[cur].para1,true)*=fetchData(inst[cur].para2);
+ break;
+ case 0x07:
+ fetchData(inst[cur].para1,true)/=fetchData(inst[cur].para2);
+ break;
+ case 0x08:
+ fetchData(inst[cur].para1,true)%=fetchData(inst[cur].para2);
+ break;
+ case 0x09:
+ fetchData(inst[cur].para1,true)&=fetchData(inst[cur].para2);
+ break;
+ case 0x0A:
+ fetchData(inst[cur].para1,true)|=fetchData(inst[cur].para2);
+ break;
+ case 0x0B:
+ fetchData(inst[cur].para1,true)^=fetchData(inst[cur].para2);
+ break;
+ case 0x0C:
+ fetchData(inst[cur].para1,true)=~fetchData(inst[cur].para1,true);
+ break;
+ case 0x0D:
+ ++fetchData(inst[cur].para1,true);
+ break;
+ case 0x0E:
+ --fetchData(inst[cur].para1,true);
+ break;
+ case 0x0F:
+ callStk.clear();
+ break;
+ case 0x11:
+ switch (fetchData(inst[cur].para1,true).type)
+ {
+ case 0:
+ fetchData(inst[cur].para1,true)=
+ fetchData(inst[cur].para1,true).ltz()?ione:izero;
+ break;
+ case 1:
+ fetchData(inst[cur].para1,true)=
+ fetchData(inst[cur].para1,true).ltz()?rone:rzero;
+ break;
+ }
+ break;
+ case 0x12:
+ switch (fetchData(inst[cur].para1,true).type)
+ {
+ case 0:
+ fetchData(inst[cur].para1,true)=
+ fetchData(inst[cur].para1,true).elz()?ione:izero;
+ break;
+ case 1:
+ fetchData(inst[cur].para1,true)=
+ fetchData(inst[cur].para1,true).elz()?rone:rzero;
+ break;
+ }
+ break;
+ case 0x13:
+ switch (fetchData(inst[cur].para1,true).type)
+ {
+ case 0:
+ fetchData(inst[cur].para1,true)=
+ fetchData(inst[cur].para1,true).gtz()?ione:izero;
+ break;
+ case 1:
+ fetchData(inst[cur].para1,true)=
+ fetchData(inst[cur].para1,true).gtz()?rone:rzero;
+ break;
+ }
+ break;
+ case 0x14:
+ switch (fetchData(inst[cur].para1,true).type)
+ {
+ case 0:
+ fetchData(inst[cur].para1,true)=
+ fetchData(inst[cur].para1,true).egz()?ione:izero;
+ break;
+ case 1:
+ fetchData(inst[cur].para1,true)=
+ fetchData(inst[cur].para1,true).egz()?rone:rzero;
+ break;
+ }
+ break;
+ case 0x15:
+ switch (fetchData(inst[cur].para1,true).type)
+ {
+ case 0:
+ fetchData(inst[cur].para1,true)=
+ fetchData(inst[cur].para1,true).eqz()?ione:izero;
+ break;
+ case 1:
+ fetchData(inst[cur].para1,true)=
+ fetchData(inst[cur].para1,true).eqz()?rone:rzero;
+ break;
+ }
+ break;
+ case 0x16:
+ switch (fetchData(inst[cur].para1,true).type)
+ {
+ case 0:
+ fetchData(inst[cur].para1,true)=
+ fetchData(inst[cur].para1,true).nez()?ione:izero;
+ break;
+ case 1:
+ fetchData(inst[cur].para1,true)=
+ fetchData(inst[cur].para1,true).nez()?rone:rzero;
+ break;
+ }
+ break;
+ case 0x21:
+ cur=fetchData(inst[cur].para1).i();
+ jmp=1;
+ break;
+ case 0x22:
+ if(fetchData(inst[cur].para1).eqz())
+ cur=fetchData(inst[cur].para2).i(),jmp=1;
+ break;
+ case 0x23:
+ if(fetchData(inst[cur].para1).nez())
+ cur=fetchData(inst[cur].para2).i(),jmp=1;
+ break;
+ }
+ if(!jmp)++cur;
+ }
+}
+void blrScriptVM::vmInit()
+{
+}
+void blrScriptVM::vmDeinit()
+{
+}
diff --git a/src/core/vmrunner.hpp b/src/core/vmrunner.hpp
new file mode 100644
index 0000000..60bc2ce
--- /dev/null
+++ b/src/core/vmrunner.hpp
@@ -0,0 +1,48 @@
+#ifndef VMRUNNER_H
+#define VMRUNNER_H
+#include "smelt.hpp"
+#include "smrandom.hpp"
+#include "scriptshared.hpp"
+const Idata ione=Idata(0,1),izero=Idata(0,0);
+const Idata rone=Idata(1,1),rzero=Idata(1,0);
+template<class memb>
+class callStack//a queue, in fact.
+{
+private:
+ int l,r;
+ memb data[16];
+public:
+ callStack(){clear();}
+ void clear(){l=0;r=-1;}
+ int size(){return r-l+1;}
+ bool empty(){return size()==0;}
+ void push(memb a){data[++r]=a;if(r>15)throw;}
+ memb pop(){if(l<=r+1)return data[l++];else throw;}
+ memb front(){return data[l];}
+ memb back(){return data[r];}
+};
+callStack<Idata> callStk;
+typedef struct _fncEntry{int hash,pos;}fncEntry;
+class blrScriptVM
+{
+private:
+ smRandomEngine* re;
+ Idata ir[101],ia[10000];
+ Idata rr[103],ra[10000];
+ SInst inst[65537];
+ int ic,fncnt,pinst;
+ fncEntry fncent[8];
+ const char *sbyte,*cbyte;
+ DWORD fsize;
+ void readPara(SPara *para);
+ int mgetc();
+ Idata& fetchData(SPara para,bool forcerw=false);
+public:
+ blrScriptVM(){ic=fncnt=pinst=0;}
+ int loadLSBFromMemory(const char* ptr,DWORD size);
+ int getInstCount();
+ void runFunction(const char *fncnym);
+ void vmInit();
+ void vmDeinit();
+};
+#endif
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..afe37d4
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,30 @@
+#include "smelt.hpp"
+#include "master/master.hpp"
+#include "master/resources.hpp"
+#include <cstdlib>
+SMELT *sm;
+sceneManager *sceneMgr;
+bool update(){return sceneMgr->update();}
+bool foc(){if(sceneMgr->isSceneActive(sceneMgr->getSceneID("GameScene")))sceneMgr->startUpdate();return false;}
+bool unfoc(){if(sceneMgr->isSceneActive(sceneMgr->getSceneID("GameScene"))&&sceneMgr->isThreadActive())sceneMgr->stopUpdate();return false;}
+int main(int argc,char **argv)
+{
+ srand(time(NULL));
+ sm=smGetInterface(SMELT_APILEVEL);
+ sceneMgr=new sceneManager;
+ resourcesLoad();
+ sm->smVidMode(960,720,true);
+ sm->smUpdateFunc(update);
+ sm->smFocFunc(foc);sm->smUnFocFunc(unfoc);
+ sm->smWinTitle("Bullet Lab Remix 3");
+ sm->smSetFPS(FPS_VSYNC);
+ sm->smInit();
+ introScene *insc=new introScene;
+ insc->introInit();
+ sm->smMainLoop();
+ sm->smFinale();
+ resourcesUnload();
+ delete sceneMgr;
+ sm->smRelease();
+ return 0;
+}
diff --git a/src/makefile b/src/makefile
new file mode 100644
index 0000000..57d9356
--- /dev/null
+++ b/src/makefile
@@ -0,0 +1,41 @@
+CC= g++
+CXXFLAGS= -std=c++11 -Wall -g -I/home/chrisoft/devel/BulletLabRemixIII/include
+LINK= -L../smelt/sdl -L../extensions -lsmeltext -lsmelt -lCxImage -lSDL2 -lvorbis -lvorbisfile -lopenal -ljpeg -lpng -lfreetype -lz -pthread
+
+all: main
+
+main: main.o scenemgr.o intro.o menuclass.o resources.o mainmenu.o bullet.o gamescene.o player.o vmrunner.o
+ $(CC) main.o scenemgr.o intro.o menuclass.o mainmenu.o resources.o bullet.o gamescene.o player.o vmrunner.o $(LINK) -o BLR3
+
+main.o: main.cpp
+ $(CC) $(CXXFLAGS) -c main.cpp
+
+scenemgr.o: master/scenemgr.cpp
+ $(CC) $(CXXFLAGS) -c master/scenemgr.cpp
+
+resources.o: master/resources.cpp
+ $(CC) $(CXXFLAGS) -c master/resources.cpp
+
+intro.o: master/intro.cpp
+ $(CC) $(CXXFLAGS) -c master/intro.cpp
+
+menuclass.o: ui/menuframework/menuclass.cpp
+ $(CC) $(CXXFLAGS) -c ui/menuframework/menuclass.cpp
+
+mainmenu.o: ui/mainmenu.cpp
+ $(CC) $(CXXFLAGS) -c ui/mainmenu.cpp
+
+bullet.o: core/bullet.cpp
+ $(CC) $(CXXFLAGS) -c core/bullet.cpp
+
+gamescene.o: core/gamescene.cpp
+ $(CC) $(CXXFLAGS) -c core/gamescene.cpp
+
+player.o: core/player.cpp
+ $(CC) $(CXXFLAGS) -c core/player.cpp
+
+vmrunner.o: core/vmrunner.cpp
+ $(CC) $(CXXFLAGS) -c core/vmrunner.cpp
+
+clean:
+ rm *.o BLR3 \ No newline at end of file
diff --git a/src/master/init.cpp b/src/master/init.cpp
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/src/master/init.cpp
@@ -0,0 +1 @@
+
diff --git a/src/master/intro.cpp b/src/master/intro.cpp
new file mode 100644
index 0000000..f8cf46b
--- /dev/null
+++ b/src/master/intro.cpp
@@ -0,0 +1,202 @@
+#include "master.hpp"
+#include "resources.hpp"
+#include <cmath>
+#include <cstdlib>
+#include <ctime>
+#include "../ui/menus.hpp"
+#define XOFFSET 75
+#define YOFFSET 100
+
+extern sceneManager *sceneMgr;
+SMELT *introScene::sm=0;
+bool introScene::sceneUpdate()
+{
+ bool done=bcnt>400;
+ for(int i=0;i<bcnt;++i)
+ {
+ pos[i]=pos[i]+vel[i];
+ vel[i]=vel[i]+acc[i];
+ if(pos[i].x>-40&&pos[i].x<1000&&pos[i].y>-40&&pos[i].y<800)done=false;
+ if(scale[i]>0.7)scale[i]-=0.1;else scale[i]=0.6;
+ }
+ if(done)
+ {
+ mainMenuScn=new mainMenuScene;
+ sceneMgr->activateScene("MainMenu");
+ delete this;
+ }
+ delay+=sm->smGetDelta();
+ if(delay<0.016)return false;
+ delay=0;
+ for(int cc=0;cc<3;++cc)
+ {
+ ++step;
+ if(ch==0)//C
+ {
+ float angle=(60-step)/45.*PI+PI/3.;
+ pos[bcnt]=smvec2d(100+XOFFSET+80*cos(angle),200+YOFFSET+80*sin(angle));
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ if(step>=60){++ch;step=0;}
+ }
+ if(ch==1)//h
+ {
+ if(step<=40)
+ {
+ pos[bcnt]=smvec2d(180+XOFFSET,120+YOFFSET+step*4);
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ }
+ if(step>40&&step<70)
+ {
+ float angle=(step-40)/30.*PI+PI;
+ pos[bcnt]=smvec2d(210+XOFFSET+30*cos(angle),220+YOFFSET+30*sin(angle));
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ }
+ if(step>=70)
+ {
+ pos[bcnt]=smvec2d(240+XOFFSET,220+YOFFSET+(step-70)*4);
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ if(step>=85){++ch;step=0;}
+ }
+ }
+ if(ch==2)//r
+ {
+ if(step<=20)
+ {
+ pos[bcnt]=smvec2d(280+XOFFSET,200+YOFFSET+step*4);
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ }
+ if(step>20)
+ {
+ float angle=(step-20)/30.*PI+PI;
+ pos[bcnt]=smvec2d(310+XOFFSET+30*cos(angle),230+YOFFSET+30*sin(angle));
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ if(step>=45){++ch;step=0;}
+ }
+ }
+ if(ch==3)//i
+ {
+ if(step<=20)
+ {
+ pos[bcnt]=smvec2d(360+XOFFSET,200+YOFFSET+step*4);
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ }
+ if(step>20)
+ {
+ pos[bcnt]=smvec2d(360+XOFFSET,180+YOFFSET);
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ ++ch;step=0;
+ }
+ }
+ if(ch==4)//s
+ {
+ if(step<=30)
+ {
+ float angle=(30-step)/30.*PI+2*PI/3.;
+ pos[bcnt]=smvec2d(450+XOFFSET+40*cos(angle),170+YOFFSET+40*sin(angle));
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ }
+ if(step>30)
+ {
+ float angle=(step-30)/30.*PI+5*PI/3.;
+ pos[bcnt]=smvec2d(410+XOFFSET+40*cos(angle),239.282+YOFFSET+40*sin(angle));
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ if(step>60){++ch;step=0;}
+ }
+ }
+ if(ch==5)
+ {
+ float angle=-step/30.*PI+3*PI/2.;
+ pos[bcnt]=smvec2d(520+XOFFSET+40*cos(angle),240+YOFFSET+40*sin(angle));
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ if(step>60){++ch;step=0;}
+ }
+ if(ch==6)
+ {
+ if(step<=30)
+ {
+ float angle=(30-step)/30.*PI+PI;
+ pos[bcnt]=smvec2d(620+XOFFSET+30*cos(angle),160+YOFFSET+30*sin(angle));
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ }
+ if(step>30&&step<=55)
+ {
+ pos[bcnt]=smvec2d(590+XOFFSET,160+YOFFSET+(step-30)*5);
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ }
+ if(step>55)
+ {
+ pos[bcnt]=smvec2d(570+XOFFSET+(step-55)*4,190+YOFFSET);
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ if(step>75){++ch;step=0;}
+ }
+ }
+ if(ch==7)
+ {
+ if(step<=20)
+ {
+ pos[bcnt]=smvec2d(700+XOFFSET,160+YOFFSET+step*5);
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ }
+ if(step>25&&step<=55)
+ {
+ float angle=(55-step)/30.*PI;
+ pos[bcnt]=smvec2d(730+XOFFSET+30*cos(angle),260+YOFFSET+30*sin(angle));
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ }
+ if(step>55)
+ {
+ pos[bcnt]=smvec2d(680+XOFFSET+(step-55)*4,190+YOFFSET);
+ vel[bcnt]=smvec2d(0,0);acc[bcnt]=smvec2d(0,0);scale[bcnt]=1.5;++bcnt;
+ if(step>75){++ch;step=0;}
+ }
+ }
+ if(ch==8)
+ {if(step>120){++ch;step=0;}}
+ if(ch==9)
+ {
+ for(int i=0;i<bcnt;++i)
+ {
+ do{acc[i]=smvec2d(rand()%100-50.,rand()%100-50.);acc[i].normalize();
+ }while(acc[i].l()<1E-6);
+ acc[i]=(rand()%100/400.+0.2)*acc[i];
+ }
+ ++ch;
+ }
+ if(ch==10)
+ {if(step>180){++ch;step=0;}}
+ }
+ return false;
+}
+bool introScene::sceneRender()
+{
+ if(ch!=11)
+ sm->smClrscr(0xFF000000);
+ else
+ {int c=step*4>255?255:step*4;sm->smClrscr(ARGB(255,c,c,c));}
+ for(int i=0;i<bcnt;++i)
+ blt->render(pos[i].x,pos[i].y,0,scale[i],scale[i]);
+ return false;
+}
+introScene::introScene()
+{
+ sm=smGetInterface(SMELT_APILEVEL);
+ pos=new smvec2d[1024];vel=new smvec2d[1024];
+ scale=new float[1024];acc=new smvec2d[1024];
+ extern const char* bsnames[];
+ smTexInfo *ti=ssanm.getTextureInfo(bsnames[rand()%8]);
+ blt=new smEntity2D(ti->tex,ti->rect);
+ blt->setCentre(12,12);
+ blt->setColor(0xC0FFFFFF);
+ sceneMgr->registerScene(this,"Intro",1000);
+}
+void introScene::introInit()
+{
+ bcnt=step=ch=0;
+ sceneMgr->activateScene("Intro");
+}
+introScene::~introScene()
+{
+ sceneMgr->deactivateScene("Intro");
+ delete[] pos;delete[] vel;delete[] acc;delete[] scale;delete blt;
+ sm->smRelease();
+}
diff --git a/src/master/master.hpp b/src/master/master.hpp
new file mode 100644
index 0000000..8ccb740
--- /dev/null
+++ b/src/master/master.hpp
@@ -0,0 +1,64 @@
+#ifndef MASTER_H
+#define MASTER_H
+#include "smelt.hpp"
+#include "smdatapack.hpp"
+#include "smmath.hpp"
+#include "smentity.hpp"
+#include "smanim.hpp"
+#include <thread>
+#include <chrono>
+struct pn{int p,n;};
+class scenePrototype
+{
+public:
+ virtual bool sceneUpdate(){return false;}
+ virtual bool sceneRender(){return false;}
+ virtual bool threadUpdate(){return false;}
+ virtual ~scenePrototype(){}
+};
+class sceneManager
+{
+private:
+ scenePrototype *sps[256];
+ static SMELT* sm;
+ static std::thread* uth;
+ char names[256][16];
+ bool active[256],thactive;
+ int scnt,dlps,lp,utime;
+ float lps,lpsud;
+ pn pq[256];
+public:
+ sceneManager();
+ ~sceneManager();
+ int registerScene(scenePrototype *sp,const char* scenename,int priority);
+ void activateScene(const char* scene);
+ void deactivateScene(const char* scene);
+ void startUpdate();
+ void stopUpdate();
+ bool isThreadActive();
+ int getSceneID(const char* scene);
+ bool isSceneActive(int sceneid);
+ void setLPS(int lps);
+ float getLPS();
+ int getTHUpdateTime();
+ bool update();
+ void threadUpdate();
+};
+
+class introScene:public scenePrototype
+{
+private:
+ char* ssptr;DWORD sssez;
+ smvec2d *pos,*vel,*acc;
+ float *scale,delay;
+ smEntity2D *blt;
+ int bcnt,step,ch;
+ static SMELT *sm;
+public:
+ introScene();
+ ~introScene();
+ void introInit();
+ bool sceneUpdate()override;
+ bool sceneRender()override;
+};
+#endif
diff --git a/src/master/resources.cpp b/src/master/resources.cpp
new file mode 100644
index 0000000..1ae35cf
--- /dev/null
+++ b/src/master/resources.cpp
@@ -0,0 +1,18 @@
+#include "smdatapack.hpp"
+#include "smbmfont.hpp"
+smDtpFileR blrdtp;
+smBMFont lcdfont;
+smAnmFile ssanm;
+void resourcesLoad()
+{
+ blrdtp.openDtp("blr3.dtp");
+ lcdfont.loadAnmFromMemory(blrdtp.getFilePtr("lcdfont.anm"),blrdtp.getFileSize("lcdfont.anm"));
+ lcdfont.setScale(0.6);
+ ssanm.loadAnmFromMemory(blrdtp.getFilePtr("ss.anm"),blrdtp.getFileSize("ss.anm"));
+}
+void resourcesUnload()
+{
+ blrdtp.closeDtp();
+ lcdfont.close();
+ ssanm.close();
+}
diff --git a/src/master/resources.hpp b/src/master/resources.hpp
new file mode 100644
index 0000000..2cfb497
--- /dev/null
+++ b/src/master/resources.hpp
@@ -0,0 +1,7 @@
+#include "smdatapack.hpp"
+#include "smbmfont.hpp"
+extern smDtpFileR blrdtp;
+extern smBMFont lcdfont;
+extern smAnmFile ssanm;
+extern void resourcesLoad();
+extern void resourcesUnload();
diff --git a/src/master/scenemgr.cpp b/src/master/scenemgr.cpp
new file mode 100644
index 0000000..1399d63
--- /dev/null
+++ b/src/master/scenemgr.cpp
@@ -0,0 +1,63 @@
+#include "master.hpp"
+#include <cstring>
+#include <cstdlib>
+int cmp(const void* a,const void* b){return ((pn*)b)->p-((pn*)a)->p;}
+extern sceneManager *sceneMgr;
+SMELT* sceneManager::sm=NULL;
+std::thread* sceneManager::uth=NULL;
+sceneManager::sceneManager(){sm=smGetInterface(SMELT_APILEVEL);utime=scnt=0;thactive=false;dlps=64;}
+sceneManager::~sceneManager(){sm->smRelease();}
+int sceneManager::registerScene(scenePrototype *sp,const char* scenename,int priority)
+{
+ for(int i=0;i<scnt;++i)if(!strncmp(scenename,names[i],16))return -1;
+ sps[scnt]=sp;
+ strncpy(names[scnt],scenename,16);
+ active[scnt]=false;
+ pq[scnt].p=priority;pq[scnt].n=scnt;++scnt;
+ qsort(pq,scnt,sizeof(pn),cmp);
+ return scnt-1;
+}
+void sceneManager::activateScene(const char* scene)
+{for(int i=0;i<scnt;++i)if(!strncmp(scene,names[i],16)){active[i]=true;break;}}
+void sceneManager::deactivateScene(const char* scene)
+{for(int i=0;i<scnt;++i)if(!strncmp(scene,names[i],16)){active[i]=false;break;}}
+void sceneManager::startUpdate(){thactive=true;uth=new std::thread(&sceneManager::threadUpdate,sceneMgr);}
+void sceneManager::stopUpdate(){thactive=false;uth->join();delete uth;}
+bool sceneManager::isThreadActive(){return thactive;}
+int sceneManager::getSceneID(const char* scene)
+{for(int i=0;i<scnt;++i)if(!strncmp(scene,names[i],16))return i;return -1;}
+bool sceneManager::isSceneActive(int sceneid){return active[sceneid];}
+void sceneManager::setLPS(int lps){dlps=lps;}
+float sceneManager::getLPS(){return lps;}
+int sceneManager::getTHUpdateTime(){return utime;}
+//Update functions return true if it wants to terminate the application.
+//Render functions should never return true.
+bool sceneManager::update()
+{
+ for(int i=0;i<scnt;++i)
+ if(active[pq[i].n])if(sps[pq[i].n]->sceneUpdate())return true;
+ sm->smRenderBegin2D(true);
+ for(int i=0;i<scnt;++i)
+ if(active[pq[i].n])sps[pq[i].n]->sceneRender();
+ sm->smRenderEnd();
+ if(sm->smGetKeyState(SMK_S)==SMKST_HIT)sm->smScreenShot("ss0.bmp");
+ return false;
+}
+void sceneManager::threadUpdate()
+{
+ long ddlay;
+ while(thactive)
+ {
+ ddlay=1000000000/dlps;
+ using namespace std::chrono;
+ high_resolution_clock::time_point b=high_resolution_clock::now();
+ for(int i=0;i<scnt;++i)
+ if(active[pq[i].n])sps[pq[i].n]->threadUpdate();
+ high_resolution_clock::time_point a=high_resolution_clock::now();
+ auto updateTime=a-b;utime=updateTime.count();
+ ++lp;lpsud+=updateTime.count()>ddlay?updateTime.count():ddlay;
+ if(lpsud>=1000000000){lps=(float)lp/(lpsud/1000000000.);lp=0;lpsud=0;}
+ if(updateTime<(duration<long,std::ratio<1l,1000000000l>>)ddlay)
+ std::this_thread::sleep_for((duration<long,std::ratio<1l,1000000000l>>)ddlay-updateTime);
+ }
+}
diff --git a/src/ui/mainmenu.cpp b/src/ui/mainmenu.cpp
new file mode 100644
index 0000000..7bb62b8
--- /dev/null
+++ b/src/ui/mainmenu.cpp
@@ -0,0 +1,69 @@
+#include "menus.hpp"
+#include "../master/resources.hpp"
+#include "../core/corepublic.hpp"
+#include <cstdlib>
+smAnmFile menubg;
+smEntity2D *menubEnt;
+smEntity2D *whitexf;
+BYTE whitea;
+bool exitPressed(int key){if((key==SMK_ENTER)||(key==SMK_Z))return true;return false;}
+bool startPressed(int key)
+//stub
+{
+ if((key==SMK_ENTER)||(key==SMK_Z))
+ {
+ extern sceneManager *sceneMgr;
+ gameScn=new gameScene;
+ sceneMgr->registerScene(gameScn,"GameScene",900);
+ sceneMgr->activateScene("GameScene");
+ sceneMgr->deactivateScene("MainMenu");
+ sceneMgr->startUpdate();
+ }
+ return false;
+}
+SMELT* mainMenuScene::sm=NULL;
+mainMenuScene::mainMenuScene()
+{
+ extern sceneManager *sceneMgr;
+ sm=smGetInterface(SMELT_APILEVEL);
+ menubg.loadAnmFromMemory(blrdtp.getFilePtr("menu.anm"),blrdtp.getFileSize("menu.anm"));
+ menubEnt=new smEntity2D(menubg.getTextureInfo("menubg")->tex,menubg.getTextureInfo("menubg")->rect);
+ whitexf=new smEntity2D(0,0,0,960,720);whitea=0xFF;whitexf->setZ(1);
+ sceneMgr->registerScene(this,"MainMenu",990);
+ menu=new menuLCD(43,20,7,360,370,&lcdfont);
+ menu->addCtrl(new menuCtrlLCD(0,20,&lcdfont));
+ menu->getCtrl(0)->setText(NULL,"START");
+ menu->getCtrl(0)->onKeyPressed(startPressed);
+ menu->addCtrl(new menuCtrlLCD(1,20,&lcdfont));
+ menu->getCtrl(1)->setText(NULL,"HIGHSCORE");
+ menu->addCtrl(new menuCtrlLCD(2,20,&lcdfont));
+ menu->getCtrl(2)->setText(NULL,"REPLAY");
+ menu->addCtrl(new menuCtrlLCD(3,20,&lcdfont));
+ menu->getCtrl(3)->setText(NULL,"CONSOLE");
+ menu->addCtrl(new menuCtrlLCD(4,20,&lcdfont));
+ menu->getCtrl(4)->setText(NULL,"NOW PLAYING");
+ menu->addCtrl(new menuCtrlLCD(5,20,&lcdfont));
+ menu->getCtrl(5)->setText(NULL,"OPTIONS");
+ menu->addCtrl(new menuCtrlLCD(6,20,&lcdfont));
+ menu->getCtrl(6)->setText(NULL,"HELP");
+ menu->addCtrl(new menuCtrlLCD(7,20,&lcdfont));
+ menu->getCtrl(7)->setText(NULL,"EXIT");
+ menu->getCtrl(7)->onKeyPressed(exitPressed);
+}
+mainMenuScene::~mainMenuScene()
+{
+ delete menu;delete menubEnt;delete whitexf;
+ menubg.close();sm->smRelease();
+}
+bool mainMenuScene::sceneUpdate(){return menu->update();}
+bool mainMenuScene::sceneRender()
+{
+ sm->smClrscr(0xFF000000);
+ if(whitea>6)whitea-=6;else whitea=0;
+ whitexf->setColor(SETA(0x00FFFFFF,whitea));
+ menubEnt->render(0,0);
+ menu->render();
+ whitexf->render(0,0);
+ return false;
+}
+mainMenuScene *mainMenuScn;
diff --git a/src/ui/menuframework/menuclass.cpp b/src/ui/menuframework/menuclass.cpp
new file mode 100644
index 0000000..d135a01
--- /dev/null
+++ b/src/ui/menuframework/menuclass.cpp
@@ -0,0 +1,115 @@
+#include "menuframework.hpp"
+#include <cstring>
+#include <cmath>
+SMELT* menuCtrlLCD::sm=NULL;
+SMELT* menuLCD::sm=NULL;
+void menuCtrlLCD::render(float x,float y)
+{
+ fnt->setColor(color);
+ fnt->render(x+skv,y,1,ALIGN_LEFT,NULL,rt);
+}
+void menuCtrlLCD::update()
+{
+ if(overlen)
+ {
+ if(selected)
+ {
+ ++scd;
+ if(scd>30)++scv,scd=0;
+ if(scv>(int)strlen(tr))scv=0;
+ char rr[48];sprintf(rr,"%s %s",tr,tr);
+ sprintf(rt,"%s %.*s",tl,(int)(maxw-strlen(tl)-1),rr+scv);
+ }
+ else
+ {
+ scv=0;
+ sprintf(rt,"%s %.*s...",tl,(int)(maxw-strlen(tl)-4),tr);
+ }
+ }
+ if(skd>0){skv=-skv;--skd;}else skd=skv=0;
+ if(selected)
+ {color=SETA(color,(int)(0x60*fabsf(sin(6*sm->smGetTime())))+0x90);}
+ else
+ color=SETA(color,0x80);
+}
+void menuCtrlLCD::setText(const char* l,const char* r)
+{
+ if(!l)l="";if(!r)r="";
+ tl=l;tr=r;
+ if((int)(strlen(l)+strlen(r)+1)>maxw)
+ {
+ overlen=true;scv=0;scd=0;
+ }
+ else
+ {
+ overlen=false;
+ sprintf(rt,"%s %*s",l,(int)(maxw-strlen(l)-1),r);
+ }
+}
+void menuLCD::addCtrl(menuCtrlLCD *ctrl)
+{
+ if(ctrl->id==0)ctrl->selected=true;else ctrl->selected=false;
+ menuCtrlLCD *last=ctrls;
+ if(!ctrls)
+ {
+ ctrls=ctrl;
+ ctrl->last=0;
+ ctrl->next=0;
+ }
+ else
+ {
+ while(last->next)last=last->next;
+ last->next=ctrl;
+ ctrl->last=last;
+ ctrl->next=0;
+ }
+ ctrl->color=0x80333333;
+}
+menuCtrlLCD* menuLCD::getCtrl(int id)
+{
+ for(menuCtrlLCD* i=ctrls;i;i=i->next)
+ if(i->id==id)return i;
+ return NULL;
+}
+bool menuLCD::update()
+{
+ if(sm->smGetKeyState(SMK_DOWN)==SMKST_HIT)
+ {
+ getCtrl(selected)->selected=false;
+ menuCtrlLCD* c;
+ for(int i=selected+1;(c=getCtrl(i));++i)
+ {if(c->enabled){selected=i;break;}}
+ getCtrl(selected)->selected=true;
+ }
+ if(sm->smGetKeyState(SMK_UP)==SMKST_HIT)
+ {
+ getCtrl(selected)->selected=false;
+ menuCtrlLCD* c;
+ for(int i=selected-1;(c=getCtrl(i));++i)
+ {if(c->enabled){selected=i;break;}}
+ getCtrl(selected)->selected=true;
+ }
+ for(menuCtrlLCD* i=ctrls;i;i=i->next)i->update();
+ if(sm->smGetKey())
+ return getCtrl(selected)->ikp();
+ else return false;
+}
+void menuLCD::render()
+{
+ int cr=rows/2+1;
+ getCtrl(selected)->render(x,y+(cr-1)*itemh);
+ menuCtrlLCD* t=getCtrl(selected);
+ for(int ccr=cr-1;ccr>0;--ccr)
+ {
+ if(t)t=getCtrl(t->id-1);
+ if(!t){fnt->setColor(0x80333333);fnt->printf(x,y+(ccr-1)*itemh,1,ALIGN_LEFT,NULL,"%*s",maxw," ");}
+ else t->render(x,y+(ccr-1)*itemh);
+ }
+ t=getCtrl(selected);
+ for(int ccr=cr+1;ccr<=rows;++ccr)
+ {
+ if(t)t=getCtrl(t->id+1);
+ if(!t){fnt->setColor(0x80333333);fnt->printf(x,y+(ccr-1)*itemh,1,ALIGN_LEFT,NULL,"%*s",maxw," ");}
+ else t->render(x,y+(ccr-1)*itemh);
+ }
+}
diff --git a/src/ui/menuframework/menuframework.hpp b/src/ui/menuframework/menuframework.hpp
new file mode 100644
index 0000000..1879686
--- /dev/null
+++ b/src/ui/menuframework/menuframework.hpp
@@ -0,0 +1,56 @@
+#ifndef BLRMENUFW_H
+#define BLRMENUFW_H
+#include "smelt.hpp"
+#include "smbmfont.hpp"
+typedef bool (*smHooki)(int);
+class menuCtrlLCD
+{
+protected:
+ static SMELT *sm;
+private:
+ smHooki keyPressedf;
+ bool overlen;
+ const char *tl,*tr;
+ int scv,scd,maxw,skv,skd;
+ char rt[64];
+public:
+ int id;
+ bool enabled,selected;
+ DWORD color;
+ menuCtrlLCD *next,*last;
+ smBMFont *fnt;
+ menuCtrlLCD(int _id,int mw,smBMFont *font)
+ {
+ sm=smGetInterface(SMELT_APILEVEL);enabled=true;keyPressedf=NULL;
+ color=0xFFFFFFFF;id=_id;maxw=mw;fnt=font;skv=0;next=last=NULL;
+ }
+ virtual ~menuCtrlLCD(){sm->smRelease();}
+ void render(float x,float y);
+ void update();
+ virtual void updateHook(){}
+ void setText(const char *l,const char *r);
+ void onKeyPressed(smHooki kpf){keyPressedf=kpf;}//parameter is the key pressed. The return value has no effect.
+ void shake(int intv,int dur){skv=intv;skd=dur;}
+ bool ikp(){if(keyPressedf)return keyPressedf(sm->smGetKey());else return false;}
+};
+class menuLCD
+{
+private:
+ static SMELT *sm;
+ float itemh,x,y;
+ int maxw,rows,selected;
+ menuCtrlLCD *ctrls;
+ smBMFont *fnt;
+public:
+ menuLCD(float ih,int mw,int rowc,float _x,float _y,smBMFont *font)
+ {
+ ctrls=NULL;selected=0;sm=smGetInterface(SMELT_APILEVEL);
+ maxw=mw;itemh=ih;rows=rowc;x=_x;y=_y;fnt=font;
+ }
+ ~menuLCD(){menuCtrlLCD *c,*n;c=ctrls;while(c){n=c->next;delete c;c=n;}sm->smRelease();}
+ void addCtrl(menuCtrlLCD *ctrl);
+ menuCtrlLCD* getCtrl(int id);
+ bool update();
+ void render();
+};
+#endif
diff --git a/src/ui/menus.hpp b/src/ui/menus.hpp
new file mode 100644
index 0000000..780a3a6
--- /dev/null
+++ b/src/ui/menus.hpp
@@ -0,0 +1,16 @@
+#include "../master/master.hpp"
+#include "menuframework/menuframework.hpp"
+extern smAnmFile menubg;
+extern smEntity2D *menubEnt;
+class mainMenuScene:public scenePrototype
+{
+private:
+ menuLCD *menu;
+ static SMELT* sm;
+public:
+ mainMenuScene();
+ ~mainMenuScene();
+ bool sceneUpdate()override;
+ bool sceneRender()override;
+};
+extern mainMenuScene *mainMenuScn;