diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..771fc31 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,60 @@ +name: build + +on: + push: + pull_request: + +jobs: + linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install SDL2 + run: | + sudo apt-get update + sudo apt-get install -y libsdl2-dev + + - name: Build + run: make + + - name: Smoke test + run: SDL_VIDEODRIVER=dummy ./cliffs --smoke-test + + windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + update: true + install: >- + make + pkgconf + mingw-w64-ucrt-x86_64-gcc + mingw-w64-ucrt-x86_64-SDL2 + + - name: Build + shell: msys2 {0} + run: make CC=gcc + + - name: Smoke test + shell: msys2 {0} + run: SDL_VIDEODRIVER=dummy ./cliffs.exe --smoke-test + + - name: Package Windows app + shell: msys2 {0} + run: | + mkdir -p dist/cliffs + cp cliffs.exe dist/cliffs/ + cp /ucrt64/bin/SDL2.dll dist/cliffs/ + cp -r assets levels dist/cliffs/ + + - name: Upload Windows app + uses: actions/upload-artifact@v4 + with: + name: cliffs-windows + path: dist/cliffs diff --git a/.gitignore b/.gitignore index 72364f9..c9b819d 100644 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,8 @@ ENV/ # Rope project settings .ropeproject + +# Native SDL build outputs +cliffs +*.o +*.dSYM/ diff --git a/CLIFFS1/AT.C b/CLIFFS1/AT.C deleted file mode 100644 index 9ca5662..0000000 --- a/CLIFFS1/AT.C +++ /dev/null @@ -1,234 +0,0 @@ -/*++AT.C is the file with main() and sub() ---*/ - -#include -#include -#include -#include -#include -#include -#include -#include "atdefs.g" - - -main() -{ -int score; - - _setvideomode(_VRES16COLOR); - - _settextposition(1,1); - printf(" ! A T T A C K T H E C L I F F S !"); - _settextposition(4,3); - printf(" CopyRite(C) 1991-1992, Peter Waksman (508)371-1890"); - - getch(); - - GameInstruct(); - - while( getch()!='' ) - { - _clearscreen(_GCLEARSCREEN); - _setcolor(BACKGROUND); - _rectangle(_GFILLINTERIOR,0,0,639,479); - - score = sub(); /*Start the program*/ - - _settextposition(1,1); - printf("TOTAL NUMBER OF MOVES = %d ",score); - getch(); - GameInstruct(); - - } - - _setvideomode(_DEFAULTMODE); - return(0); -} - -int sub(void) -{ -U_DATA keys,moves; -TM_DATA tm,clock; -LEDGE *ledges; -ROPE rope; -TIEPOINT *pitons; -MAN man[2],*currentMan; -BELAY belay[2]; -int nledges; -int direction=RIGHT; -int movecount = 0; -int manflag=1,manindex=0; -int flag=1,turnstatus=READY; -int belaykey=0,pitonkey=0; - -/*******************************INITIALIZE*********************************/ - - keys.data = (int *)malloc(2*MX_KEYS); /*allocate memory*/ - moves.data = (int *)malloc(2*MX_MOVES); - ledges = (LEDGE *)malloc(6*MX_LEDGES); - pitons = (TIEPOINT *)malloc( sizeof(TIEPOINT)*MX_PITONS ); - - keys.i = 0; /*init indeces*/ - moves.i = 0; - moves.data[0] = 0; - - clock.status = START; - keys.status = READY; /*init statuses*/ - moves.status = READY; - tm.status = READY; - - DisplayClock(0); - - FlipDirection(&direction,77); /*displays the arrow*/ - BelayKey(1); /*and status displays*/ - - nledges = ReadLedges(ledges,"ledges"); /*read the "ledges" file*/ - DisplayLedges(ledges,nledges); - - InitMan(&man[0],110,250); /*init DougalH*/ - InitBelay(&belay[0],&man[0]); - DisplayMan(&man[0],HOTMAN); - - InitMan(&man[1],70,250); /*init PeterB*/ - InitBelay(&belay[1],&man[1]); - DisplayMan(&man[1],COLDMAN); - - InitRope(&rope,70,250,110,250); /*init rope*/ - - BelayToRope(&rope,&(belay[0].b) ,3); /*belay DougalH's body to rope*/ - BelayToRope(&rope,&(belay[1].b) ,3); /*belay PeterB's body to rope*/ - - DangleEnds(&rope); - - DisplayRope(&rope,ROPECOLOR); - DisplayBelayStatus(&belay[0],191,457); - DisplayBelayStatus(&belay[1],71,457); - - DisplayMoveCount(0); - - InitPitons(pitons); /*init Pitons*/ - - currentMan = &man[0]; - -/**************************START MAIN LOOP*****************************/ - - - while( moves.status != DONE ) - { - key_proc(&keys); - move_proc( &moves, keys.data[keys.i]); - tm_proc(&tm,KEYRATE); - turn_proc(&direction,keys.data[keys.i],&turnstatus); - - main_exec( &keys, &moves, &tm, &turnstatus, &manflag, &belaykey, &pitonkey); - - if( tm.status==READY ) /*if NOT moving*/ - { - clock_proc(&clock); - if( clock.status==HOT ) - DisplayClock( TimeDiff(&clock.end,&clock.begin) ); - - man_exec(&manflag, &manindex, man, ¤tMan); - belay_exec(&rope,belay,manindex,&belaykey); - piton_exec(&pitonkey,pitons,currentMan,&rope,ledges,nledges); - } - else if( tm.status==DONE ) /*if moving*/ - { - movecount++; - DisplayMoveCount(movecount); - - EraseScene(currentMan, &rope); - - flag = MoveMan(currentMan,&moves,direction,ledges,nledges, - &rope,belay,manindex); - ReDisplayScene(manindex,man,&rope,ledges,nledges); - - tm.status = READY; /*re-init the moves*/ - moves.i = 0; - } - } - -/****************************END THE GAME***************************/ - free( keys.data ); - free( moves.data ); - free( pitons ); - - if( flag==-1 ) - fall(currentMan); - else if( flag==0 - &&( currentMan->Y + currentMan->lf.Y ==ledges[0].height - || currentMan->Y + currentMan->rf.Y ==ledges[0].height) ) - Congratulations(); - else - Retreat(); - - return(movecount); -} - - -turn_proc(int *direction,int key,int *turnstatus) -{ - if( *turnstatus!=HOT ) - return(0); - FlipDirection( direction , key); - *turnstatus = READY; - return(0); -} - - -main_exec(U_DATA *keys, U_DATA *moves, TM_DATA *tm, int *turnstatus, - int *manflag, int *belaykey, int *pitonkey) -{ - if( keys->status!=HOT ) - return(0); - switch( keys->data[keys->i] ) - { - case '': - moves->status = DONE; - break; - case 'o': - *belaykey = 'o'; - break; - case 'w': - *belaykey = 'w'; - break; - case 'b': - *belaykey = 'b'; - break; - case 'i': - *pitonkey = 'i'; - break; - case 'e': - *pitonkey = 'e'; - break; - case 72: - case 80: - case 75: - case 77: -/* FlipDirection( direction , keys->data[keys->i]);*/ - *turnstatus = HOT; - break; - case 'p': - case 'l': - case 'k': - case 'm': - case 'q': - case 'a': - case 's': - case 'x': - moves->status = M_HOT; - tm->status = START; - break; - case 60: /*F2 key*/ - *manflag = 1; - break; - case 59: /*F1 key*/ - *manflag = 2; - break; - } - return(0); -} - - - - \ No newline at end of file diff --git a/CLIFFS1/ATDEFS.G b/CLIFFS1/ATDEFS.G deleted file mode 100644 index 5513412..0000000 --- a/CLIFFS1/ATDEFS.G +++ /dev/null @@ -1,226 +0,0 @@ -/*++ ATDEFS.G is the file defining constants, structures and function -prototypes. ---*/ - -#define START 0 -#define READY 1 -#define BUSY 2 -#define HOT 3 -#define DONE 4 -#define M_HOT 5 -#define C_HOT 6 - - -#define UP 1 -#define DOWN 2 -#define LEFT 3 -#define RIGHT 4 - -#define INROPE 1 -#define OUTROPE -1 -#define END0 1 -#define ENDL -1 - -#define T_OFF 0 -#define T_ON_MAN 1 -#define T_ON_MAN_SLIDING 2 /*used for body but not hands*/ -#define T_ON_LEDGE 3 -#define T_ON_LEDGE_SLIDING 4 - -#define R_OFF 0 -#define R_ON 1 -#define R_SLIDING 2 - -#define MX_TIES 20 -#define MX_PITONS 20 -#define MX_KEYS 80 -#define MX_MOVES 16 -#define MX_LEDGES 500 -#define MX_VEXT 20 /*max vertical extension*/ -#define MX_HEXT 10 /*max horizontal extension*/ -#define DEXT 10 - -#define KEYRATE 0.15 - -/*******************COLORS*****************/ - -#define BLACK 0 -#define WHITE 15 -#define YELLOW 14 -#define LBLUE 11 -#define RED 12 -#define GREY 8 -#define BLUE 1 -#define GREEN 2 -#define ORANGE 4 -#define HOTMAN YELLOW -#define COLDMAN LBLUE -#define HOTTEXT YELLOW -#define COLDTEXT LBLUE -#define LEDGECOLOR GREY -#define BACKGROUND BLACK -#define ROPECOLOR RED -#define FIXEDCOLOR YELLOW -#define SLIDINGCOLOR LBLUE -#define ARROWCOLOR YELLOW -/**************************************/ - -typedef struct -{ -int status; -struct dostime_t begin; -struct dostime_t end; -}TM_DATA; - -typedef struct -{ -int status; -int i; /*the "current" index*/ -int *data; -}U_DATA; /*after mallocing *(data + i) should mean something*/ - - -typedef struct -{ -int LR; /*left right sign*/ -int DU; /*down up sign*/ -int X, Y; /*limb extensions*/ -}LIMB ; - -typedef struct -{ -int X,Y; -LIMB lh,rh,lf,rf; -LIMB Olh,Orh,Olf,Orf; -}MAN; - -typedef struct -{ -int height,leftend,rightend; -}LEDGE; - - -typedef struct tie_struct -{ -int status; -int *x,*y; -struct tie_struct *pre,*post; -}TIE; - -typedef struct -{ -int x0,y0,xL,yL; -int end0,endL; -TIE tie[MX_TIES]; -}ROPE; - -typedef struct -{ -int status; -int x,y; -TIE *tie; -}TIEPOINT; - -typedef struct -{ -TIEPOINT l,b,r; -}BELAY; - - -int main(void); -int putpik(char *pikname, int x, int y); -int sub(void); -int MoveMan(MAN *man, U_DATA *moves,int direction, LEDGE ledges[],int N, ROPE *rope, BELAY belay[],int manindex); -int ExecuteMoves(int *dX, int *dY, MAN *man, U_DATA *moves,int direction, LEDGE ledges[],int N); -int MoveLimb(LIMB *limb, int sign, int direction ); -int main_exec(U_DATA *keys, U_DATA *moves, TM_DATA *tm, int *turnstatus, int *manflag, int *belaykey, int *pitonkey); -int FlipDirection(int *direction, int c); -int DisplayArrow(int x, int y, int direction,int color); -int move_proc(U_DATA *moves, int c); -int CopyAllLimbs(MAN *man); -int isAttached(MAN *man, LIMB *limb,LEDGE ledges[],int N); -int limbCopy(LIMB *source,LIMB *dest); -int key_proc(U_DATA *keys); -int tm_proc(TM_DATA *tm , double TimeLimit ); -int InitMan(MAN *man,int x, int y); -int DisplayLedges(LEDGE ledges[],int n); -int DisplayMan(MAN *man,int color); -int settleWeight(MAN *man,LEDGE ledges[],int N); -int swingBod(int *dX, int *dY, LIMB *new, LIMB *old,int direction); -int fall(MAN *man); -int Congratulations(void); -int Retreat(void); -int ReadLedges(LEDGE ledges[], char string[]); /*returns the number of ledges*/ -int GameInstruct(void); -int MiniKey(void); -int man_exec(int *manflag, int *manindex, MAN man[], MAN **currentMan); -int UpdataMovingMan(MAN *currentMan,U_DATA *moves,int direction,LEDGE ledges[],int nledges); -int MoveLimbKey(int move, MAN *man, int direction); -int LedgeSupport(MAN *man,LEDGE ledges[],int N); -int turn_proc(int *direction,int key,int *turnstatus); - - - -int EraseScene(MAN *man,ROPE *rope); -int ReDisplayScene(int manindex,MAN man[],ROPE *rope,LEDGE ledges[],int nledges); - -int belay_exec(ROPE *rope, BELAY *belay, int manindex, int *belaykey); -int InitRope(ROPE *rope,int x0, int y0, int xL, int yL); -int DisplayRope(ROPE *rope, int color); -int DeleteTie(TIE *tie); -int InsertTie(TIE *at, TIE *tie); -int UpdateBelay(BELAY *belay, MAN *man); -int InitBelay(BELAY *belay,MAN *man); -int DisplayBelayStatus(BELAY *belay, int Ox, int Oy); -int BelayToRope(ROPE *rope, TIEPOINT *t, int statecount); -int GetFreeIndex(ROPE *rope); -int CheckRopeTouch(int x, int y, ROPE *rope,TIE **at); -int BelayKey(int manflag); -int isNearRope(int a, int b, int x1, int y1, int x2, int y2); -int DisplayMoveCount(int count); -int AddTiepoint(TIE *at, TIE *tie, TIEPOINT *t,int status); -int DangleEnds(ROPE *rope); - -int piton_exec(int *pitonkey,TIEPOINT pitons[],MAN *man,ROPE *rope, LEDGE ledges[],int nledges); -int InitPitons(TIEPOINT pitons[]); -int DisplayPitons(TIEPOINT pitons[]); -int GetPitonIndex(TIEPOINT pitons[], int x, int y); -int PitonToRope(TIEPOINT *piton,ROPE *rope,TIE *at,int x,int y); -int UpdateRopePitonStatus(ROPE *rope,TIEPOINT pitons[]); - -/*int rope_exec(ROPE *rope,int *ropekey, BELAY belay[],TIEPOINT pitons[],int manindex);*/ - -#ifdef WAIT -int ExecuteRopeMoves(int *dX, int *dY, ROPE *rope,BELAY *belay, MAN *man, int direction, int manindex); -int isPulling( TIEPOINT *t, LIMB *limb, LIMB *oldlimb, int direction,int *dX, int *dY); -int TestRopePull(TIEPOINT *t, int direction, int *in, int *out); -int DoPull(TIEPOINT *t,TIEPOINT *b,TIEPOINT *ob,ROPE *rope,int dir,int *dX,int *dY,int hX,int hY); -int LedgeSupport(MAN *man,LEDGE ledges[],int N); -int RopeSupport(BELAY *belay,int *pivX,int *pivY); -int tmpSetPivot(TIEPOINT *t,int *tin,int *tout,int *x,int *y,int in,int out); -int SetPivot(int *pivX,int *pivY,int xl,int yl,int xr,int yr,int xb,int yb); -int TestBootstrap(TIEPOINT *t,TIE *u,TIE *v,int in,int out,int *rflag); -#endif - -int NewSettleWeight(MAN *man,BELAY belay[],LEDGE ledges[],int N,ROPE *rope,int manindex); -int TestPull(ROPE *rope,BELAY *belay,TIEPOINT *otherbod,int *a,int *b,int dir,int *lift); -int NextTie(TIE **tie,int dir); -int HangDrop(MAN *man, BELAY *belay,TIEPOINT *otherbod,LEDGE ledges[],int N,ROPE *rope); -int ReplaceMan(MAN *man,ROPE *rope,BELAY *belay,int x,int y); -int isPivotForward(int a, int b , int handX, int handY, int direction); -int AdjustRopeEnds(ROPE *rope,int length,int end,int sign); -int Circle(int x,int a,int b,double r); -int SwingBaby(int a,int b,int ilift, int olift, MAN *man,BELAY *belay,ROPE *rope, LEDGE ledges[],int N); -int OnBelay(BELAY *belay); -int StartFall(MAN *man,BELAY *belay,LEDGE ledges[],int N,ROPE *rope); -int DoPull(int test,int a,int b,int direction,TIEPOINT *t, ROPE *rope,int *hX, int *hY, int sign); -int ExecuteRopeMoves(int *dX, int *dY, MAN *man, BELAY belay[],ROPE *rope,int manindex, int direction,int ledgesupport); -int LimbRopeMove(LIMB *limb,LIMB *oldlimb,BELAY *belay,ROPE *rope,TIEPOINT *t, TIEPOINT *otherbod,int *dX,int *dY,int direction); -int CheckLimbCentering(TIEPOINT *t,LIMB *hand,int ledgesupport); - -int clock_proc(TM_DATA *clock); -int DisplayClock(int time); -int TimeDiff(struct dostime_t *new,struct dostime_t *old); - -int I_DisplayLedges(LEDGE ledges[],int N,MAN *man,int direction,int a1,int b1, int a2, int b2,int color); - \ No newline at end of file diff --git a/CLIFFS1/ATI.C b/CLIFFS1/ATI.C deleted file mode 100644 index 9de249c..0000000 --- a/CLIFFS1/ATI.C +++ /dev/null @@ -1,215 +0,0 @@ -/*++ATI.C is the file with main() and sub() for "I" point of view. ---*/ - -#include -#include -#include -#include -#include -#include -#include -#include "atdefs.g" - -int imain_exec(U_DATA *keys, U_DATA *moves, TM_DATA *tm, int *turnstatus); -int iMoveMan(MAN *man, U_DATA *moves,int direction, LEDGE ledges[],int N); -int turn_exec(int *direction,int key,int *turnstatus,LEDGE ledges[],int N,MAN *man); - -#define WX1 440 -#define WY1 275 -#define WX2 620 -#define WY2 435 - - -main() -{ -int score; - - _setvideomode(_VRES16COLOR); - - _settextposition(1,1); - printf(" ! A T T A C K T H E C L I F F S !"); - _settextposition(3,3); - printf(" CopyRite(C) 1992 Peter Waksman "); - - GameInstruct(); -/* MiniKey(); */ - - while(getch()!='' ) - { - _clearscreen(_GCLEARSCREEN); - _setcolor(BACKGROUND); - _rectangle(_GFILLINTERIOR,0,0,639,479); - score = sub(); - - _clearscreen(_GCLEARSCREEN); - _settextposition(1,1); - printf("TOTAL NUMBER OF MOVES = %d ",score); - - GameInstruct(); - } - _setvideomode(_DEFAULTMODE); - return(0); -} - -int sub(void) -{ -U_DATA keys,moves; -TM_DATA tm,clock; -LEDGE *ledges; -MAN man[2],*currentMan; -int nledges; -int direction=RIGHT; -int movecount = 0; -int manflag=1,manindex=0; -int flag=1,turnstatus=READY; -int belaykey=0,pitonkey=0; - - keys.data = (int *)malloc(2*MX_KEYS); /*allocate memory*/ - moves.data = (int *)malloc(2*MX_MOVES); - ledges = (LEDGE *)malloc(6*MX_LEDGES); - - keys.i = 0; /*init indeces*/ - moves.i = 0; - moves.data[0] = 0; - - clock.status = START; - keys.status = READY; /*init statuses*/ - moves.status = READY; - tm.status = READY; - - DisplayClock(0); - - FlipDirection(&direction,77); /*displays the arrow*/ - - nledges = ReadLedges(ledges,"ledges"); /*read the "ledges" file*/ -// DisplayLedges(ledges,nledges); - - InitMan(&man[0],110,250); /*init DougalH*/ - DisplayMan(&man[0],HOTMAN); - - DisplayMoveCount(0); - - currentMan = &man[0]; - - I_DisplayLedges(ledges,nledges,currentMan,direction,WX1,WY1,WX2,WY2,LEDGECOLOR); - -/**************************END OF INITIALIZATION**********************/ - - while( moves.status != DONE ) - { - key_proc(&keys); - move_proc( &moves, keys.data[keys.i]); - tm_proc(&tm,KEYRATE); /*had 0.15*/ - - imain_exec( &keys, &moves, &tm, &turnstatus); - - if( tm.status==READY ) /*if NOT moving*/ - { - clock_proc(&clock); - if( clock.status==HOT ) - DisplayClock( TimeDiff(&clock.end,&clock.begin) ); - - turn_exec(&direction,keys.data[keys.i],&turnstatus, - ledges,nledges,currentMan); - } - else if( tm.status==DONE ) /*if moving*/ - { - movecount++; - DisplayMoveCount(movecount); - - DisplayMan(currentMan, 0); - - flag = iMoveMan(currentMan,&moves,direction,ledges,nledges); - -// DisplayLedges(ledges,nledges); - DisplayMan(currentMan, 14); - - I_DisplayLedges(ledges,nledges,currentMan,direction, - WX1,WY1,WX2,WY2,LEDGECOLOR); - - tm.status = READY; /*re-init the moves*/ - moves.i = 0; - } - } - - free( keys.data ); - free( moves.data ); - /*ENDGAMES*/ - if( flag==-1 ) - fall(currentMan); - else if( flag==0 - &&( currentMan->Y + currentMan->lf.Y ==ledges[0].height - || currentMan->Y + currentMan->rf.Y ==ledges[0].height) ) - Congratulations(); - else - Retreat(); - - return(movecount); -} - - -turn_exec(int *direction,int key,int *turnstatus, LEDGE ledges[],int N,MAN *man) -{ - if( *turnstatus!=HOT ) - return(0); - FlipDirection( direction , key); - - I_DisplayLedges(ledges,N,man,*direction,WX1,WY1,WX2,WY2,LEDGECOLOR); - - *turnstatus = READY; - return(0); -} - -imain_exec(U_DATA *keys, U_DATA *moves, TM_DATA *tm, int *turnstatus) -{ - if( keys->status!=HOT ) - return(0); - switch( keys->data[keys->i] ) - { - case '': - moves->status = DONE; - break; - case 72: - case 80: - case 75: - case 77: -/* FlipDirection( direction , keys->data[keys->i]);*/ - *turnstatus = HOT; - break; - case 'p': - case 'l': - case 'k': - case 'm': - case 'q': - case 'a': - case 's': - case 'x': - moves->status = M_HOT; - tm->status = START; - break; - } - return(0); -} - - -iMoveMan(MAN *man, U_DATA *moves,int direction, LEDGE ledges[],int N) -{ -int dX=0,dY=0; -int ret=0; - /*move limbs and calculate body change if attached*/ - ExecuteMoves(&dX, &dY, man, moves, direction, ledges, N); - - man->X -=dX; /*adjust body position if appropriate*/ - man->Y +=dY; - - CopyAllLimbs(man); /*and update limb history*/ - - ret = settleWeight(man,ledges,N); - - if( ret==-1 ) - moves->status = DONE; - - return(ret); -} - - \ No newline at end of file diff --git a/CLIFFS1/ATMOVE.C b/CLIFFS1/ATMOVE.C deleted file mode 100644 index 4a165cd..0000000 --- a/CLIFFS1/ATMOVE.C +++ /dev/null @@ -1,295 +0,0 @@ -/*++ -ATMOVE.C is the file for man's movements ---*/ - -#include -#include -#include -#include -#include -#include -#include "atdefs.g" - - -man_exec(int *manflag, int *manindex, MAN man[], MAN **currentMan) -{ - if( *manflag==0 ) - return(0); - else if( *manflag==1 ) /*DougalH is moving*/ - { - *manindex = 0; - *currentMan = &man[0]; - DisplayMan(&man[0],HOTMAN); - DisplayMan(&man[1],COLDMAN); /*and PeterB is freezing*/ - BelayKey(1); - } - else if( *manflag==2) /*or PeterB is moving*/ - { - *manindex = 1; - *currentMan = &man[1]; - DisplayMan(&man[1],HOTMAN); - DisplayMan(&man[0],COLDMAN); /*and DougalH is freezing*/ - BelayKey(2); - } - - *manflag=0; - return(0); -} - -MoveMan(MAN *man, U_DATA *moves,int direction, LEDGE ledges[],int N, - ROPE *rope, BELAY belay[],int manindex) -{ -int dX=0,dY=0; -int ret=0; -int ledgesupport; - /*move limbs and calculate body change rel ledges*/ - ExecuteMoves(&dX, &dY, man, moves, direction, ledges, N); - - /*or calculate pull rel rope*/ - if( dX==0 && dY==0 ) - { - ledgesupport = LedgeSupport(man,ledges,N); - ExecuteRopeMoves(&dX,&dY,man,belay,rope,manindex,direction,ledgesupport); - } - man->X -=dX; /*adjust body position if appropriate*/ - man->Y +=dY; - - CopyAllLimbs(man); /*and update limb history*/ - UpdateBelay(&belay[manindex],man); /*and update belay*/ - DangleEnds(rope); /*and update rope ends*/ - - ret = NewSettleWeight(man,belay,ledges,N,rope,manindex); - -/* ret = settleWeight(man,ledges,N); SAVE FOR ROPELESS CODE*/ - - if( ret==-1 ) - moves->status = DONE; - - return(ret); -} - -ExecuteMoves(int *dX, int *dY, MAN *man, U_DATA *moves,int direction, - LEDGE ledges[],int N) -{ -int i; -int rhdX=0,rhdY=0,lhdX=0,lhdY=0,rfdX=0,rfdY=0,lfdX=0,lfdY=0; - - for(i=0;ii;i++) - MoveLimbKey(moves->data[i],man,direction); - - if( isAttached( man, &man->Orh, ledges, N ) ) - swingBod( &rhdX, &rhdY, &man->rh, &man->Orh ,direction); - if( isAttached( man, &man->Olh, ledges, N ) ) - swingBod( &lhdX, &lhdY, &man->lh, &man->Olh , direction); - if( isAttached( man, &man->Orf, ledges, N ) ) - swingBod( &rfdX, &rfdY, &man->rf, &man->Orf , direction); - if( isAttached( man, &man->Olf, ledges, N ) ) - swingBod( &lfdX, &lfdY, &man->lf, &man->Olf , direction); - - if( rhdX ) - *dX = rhdX; - else if( lhdX ) - *dX = lhdX; - else if( rfdX ) - *dX = rfdX; - else if( lfdX ) - *dX = lfdX; - else - *dX = 0; - - if( rhdY ) - *dY = rhdY; - else if( lhdY ) - *dY = lhdY; - else if( rfdY ) - *dY = rfdY; - else if( lfdY ) - *dY = lfdY; - else - *dY = 0; - - return(0); -} - -MoveLimbKey(int move, MAN *man, int direction) -{ -LIMB *limb; -int sign; - switch( move ) - { - case 'p': /*RIGHT HAND*/ - limb = &man->rh; - sign = 1; - break; - case 'l': - limb = &man->rh; - sign = -1; - break; - case 'q': /*LEFT HAND*/ - limb = &man->lh; - sign = 1; - break; - case 'a': - limb = &man->lh; - sign = -1; - break; - - case 'k': /*RIGHT FOOT*/ - limb = &man->rf; - if( direction==UP || direction==DOWN ) - sign = -1; - else - sign = 1; - break; - case 'm': - limb = &man->rf; - if( direction==UP || direction==DOWN ) - sign = 1; - else - sign = -1; - break; - case 's': /*LEFT FOOT*/ - limb = &man->lf; - if( direction==UP || direction==DOWN ) - sign = -1; - else - sign = 1; - break; - case 'x': - limb = &man->lf; - if( direction==UP || direction==DOWN ) - sign = 1; - else - sign = -1; - break; - } - - MoveLimb( limb, sign, direction ); - - return(0); -} - - -MoveLimb(LIMB *limb, int sign, int direction ) -{ -int a; - if( direction==LEFT || direction==RIGHT ) - { - a = limb->X; - - a += sign*DEXT; - if( a<0 ) - a = 0; - if( a>MX_HEXT ) - a = MX_HEXT; - - limb->X = a; - } - else /*if UP or DOWN*/ - { - a = limb->Y; - - a += sign*DEXT; - if( a<0 ) - a = 0; - if( a>MX_VEXT ) - a = MX_VEXT; - - limb->Y = a; - } - return(0); -} - -CopyAllLimbs(MAN *man) -{ - limbCopy(&man->rh,&man->Orh); - limbCopy(&man->lh,&man->Olh); - limbCopy(&man->lf,&man->Olf); - limbCopy(&man->rf,&man->Orf); - return(0); -} -limbCopy(LIMB *source,LIMB *dest) -{ - dest->LR = source->LR; - dest->DU = source->DU; - dest->X = source->X; - dest->Y = source->Y; - - return(0); -} - -isAttached(MAN *man, LIMB *limb,LEDGE ledges[],int N) -{ -int i; -int x,y; - x = man->X + limb->LR * limb->X; - y = man->Y - limb->DU * limb->Y; - for(i=0;i= ledges[i].leftend - && x <= ledges[i].rightend ) - return(1); - } - return(0); -} - -swingBod(int *dX, int *dY, LIMB *new, LIMB *old,int direction) -{ - -int d = new->X - old->X; -int e = new->Y - old->Y; - - d *= new->LR; /*same as old->LR*/ - e *= new->DU; - - if( ( d>0 && direction==LEFT ) - || ( d<0 && direction==RIGHT ) - ) - *dX = d; - if( ( e>0 && direction==DOWN ) - || ( e<0 && direction==UP ) - ) - *dY= e; - - return(0); -} - -LedgeSupport(MAN *man,LEDGE ledges[],int N) -{ -int attcount = 0; - if( isAttached(man,&man->rh, ledges, N) ) - attcount +=1; - if( isAttached(man,&man->lh, ledges, N) ) - attcount +=1; - if( isAttached(man,&man->lf, ledges, N) ) - attcount +=2; - if( isAttached(man,&man->rf, ledges, N) ) - attcount +=2; - - return(attcount); -} - -settleWeight(MAN *man,LEDGE ledges[],int N) -{ -int attcount; - while(1) - { - DisplayMan(man,HOTMAN); - - attcount = LedgeSupport(man,ledges, N); - - if( attcount>2 ) - break; - - if( man->Y > 350 ) - return(-1); - - DisplayMan(man,BACKGROUND); - - (man->Y)++; - } - return(0); -} - - \ No newline at end of file diff --git a/CLIFFS1/ATROPE.C b/CLIFFS1/ATROPE.C deleted file mode 100644 index 83e640c..0000000 --- a/CLIFFS1/ATROPE.C +++ /dev/null @@ -1,445 +0,0 @@ -/*++ATROPE.C deals with the mechanics of attaching the rope to "belays" -(parts of the man: lefthand, righthand, body) and to "pitons" (attachments -to ledges). ---*/ - -#include -#include -#include -#include -#include -#include - -#include "atdefs.g" - -/********************************BELAYS******************************/ -belay_exec(ROPE *rope, BELAY belay[], int manindex, int *belaykey) -{ -int statecount; -TIEPOINT *t; -BELAY *blay; - if( *belaykey==0 ) - return(0); - if( *belaykey=='e' || *belaykey=='i' ) - { - - *belaykey=0; - return(0); - } - - blay = &belay[manindex]; - - DisplayRope(rope,BACKGROUND); - - switch( *belaykey ) - { - case 'w': /*left hand*/ - t = &(blay->l); - statecount=2; - break; - case 'b': /*body*/ - t = &(blay->b); - statecount=3; - break; - case 'o': /*right hand*/ - t = &(blay->r); - statecount=2; - break; - } - - BelayToRope(rope, t, statecount); - - DangleEnds(rope); - - DisplayRope(rope,ROPECOLOR); - DisplayBelayStatus(&belay[0],191,457); - DisplayBelayStatus(&belay[1],71,457); - - *belaykey = 0; - return(0); -} - -BelayToRope(ROPE *rope, TIEPOINT *t, int statecount) -{ -int i, status=t->status; -TIE *at; - if( status==T_ON_MAN && statecount==3 ) - { - t->status = T_ON_MAN_SLIDING; - (t->tie)->status = T_ON_MAN_SLIDING; - return(0); - } - else if( status==T_ON_MAN || status==T_ON_MAN_SLIDING ) - { - if( DeleteTie(t->tie) ) - t->status =T_OFF; - return(0); - } - /*else status==T_OFF and we want to add a tie*/ - if( CheckRopeTouch(t->x,t->y,rope,&at) < 0 ) - return(-1); - if( (i=GetFreeIndex(rope))==MX_TIES ) - return(-1); - - AddTiepoint(at,&(rope->tie[i]),t,T_ON_MAN); - - return(0); -} - -DeleteTie(TIE *tie) -{ - if( tie->pre==NULL || tie->post==NULL ) - return(0); - - tie->status = T_OFF; - - (tie->post)->pre = tie->pre; - (tie->pre)->post = tie->post; - return(1); -} - - -AddTiepoint(TIE *at, TIE *tie, TIEPOINT *t,int status) -{ - if( InsertTie(at,tie) ) - { - tie->x = &(t->x); /*attach tie to tiepoint*/ - tie->y = &(t->y); - t->status= status; - t->tie = tie; /*attach tiepoint to tie*/ - (t->tie)->status = status; - return(1); - } - else - return(0); -} - -InitRope(ROPE *rope, int x0,int y0, int xL, int yL) -{ - rope->x0 = x0; - rope->y0 = y0; - rope->xL = xL; - rope->yL = yL; - - rope->end0 = 10; - rope->endL = 10; - - rope->tie[0].x = &(rope->x0); - rope->tie[0].y = &(rope->y0); - rope->tie[1].x = &(rope->xL); - rope->tie[1].y = &(rope->yL); - - rope->tie[0].status = T_OFF; - rope->tie[1].status = T_OFF; - - rope->tie[0].pre = NULL; - rope->tie[0].post= &(rope->tie[1]); - rope->tie[1].pre = &(rope->tie[0]); - rope->tie[1].post= NULL; - - return(0); -} - -DangleEnds(ROPE *rope) -{ -TIE *tie; - tie = rope->tie[0].post; - rope->x0 = *(tie->x); - rope->y0 = (*(tie->y))+rope->end0; - - tie = rope->tie[1].pre; - rope->xL = *(tie->x); - rope->yL = (*(tie->y))+rope->endL; - - return(0); -} - - -InsertTie(TIE *at, TIE *tie) -{ - if( at->post !=NULL ) /*insert "tie" after "at" */ - { - tie->pre = at; /*link tie between at and at->post*/ - tie->post = at->post; - - (at->post)->pre = tie; /*fix the "up" link of at->post*/ - at->post = tie; /*and the "down" link of at*/ - return(1); - } - else if( at->pre!=NULL ) /*or insert "tie" before "at" */ - { - tie->post = at; - tie->pre = at->pre; - (at->pre)->post = tie; - at->pre = tie; - return(1); - } - return(0); -} - -CheckRopeTouch(int x, int y, ROPE *rope, TIE **at) -{ -TIE *tie,*sie; - tie = &( rope->tie[0] ); - sie = tie->post; - while( sie!=NULL ) /*looping through rope with adjacent ties*/ - { - if( isNearRope(x,y,*tie->x,*tie->y,*sie->x,*sie->y) ) - { - *at = tie; - return(0); - } - tie = sie; - sie = sie->post; - } - return(-1); -} - -isNearRope(int a, int b, int x1, int y1, int x2, int y2) -{ -int p, tol=4; - if( x1==x2 ) /*rope is vertical*/ - { - if( a!=x1 ) /*test x-coordinate*/ - return(0); - if( (b-y1)*(b-y2)>0 ) /*test between-ness of y-coords*/ - return(0); - return(1); - } - - if( (a-x1)*(a-x2)>0 ) /*test between-ness of x-coords*/ - return(0); - /*find point on sloping rope*/ - p = y1 + (int)( (double)(a-x1) * (double)(y2-y1)/(double)(x2-x1) ); - - if( p-tol<=b && p+tol>=b ) - return(1); - else - return(0); -} - - -InitBelay(BELAY *belay, MAN *man) -{ - belay->l.status = T_OFF; - belay->b.status = T_OFF; - belay->r.status = T_OFF; - - UpdateBelay(belay,man); - - return(0); -} - -UpdateBelay(BELAY *belay, MAN *man) -{ -int x=man->X,y=man->Y; - (belay->b).x = x; - (belay->b).y = y; - (belay->l).x = x - (man->lh.X); - (belay->l).y = y - (man->lh.Y); - (belay->r).x = x + (man->rh.X); - (belay->r).y = y - (man->rh.Y); - return(0); -} - -GetFreeIndex(ROPE *rope) -{ -int i; -TIE *tie,*test; - for(i=0;itie[i]); - tie = &(rope->tie[0]); - while(tie!=NULL) /*loop through all ties on rope*/ - { - if( tie==test ) - break; - tie = tie->post; - } - /*if you went through rope without seeing "tie[i]"*/ - if( tie==NULL ) - break; - } - return(i); -} - - -DisplayRope(ROPE *rope,int color) -{ -TIE *tie; - - _setcolor(color); - - tie = &(rope->tie[0]); - _moveto( *tie->x, *tie->y); - tie = tie->post; - - while( tie!= NULL ) - { - _lineto( *tie->x, *tie->y); - tie = tie->post; - } - return(0); -} - - - -/************************PITONS**************************************/ -piton_exec(int *pitonkey,TIEPOINT pitons[],MAN *man,ROPE *rope, - LEDGE ledges[],int nledges) -{ -int x,y,i; -TIE *at; - if( *pitonkey==0 ) - return(0); - switch( *pitonkey ) - { - case 'i': - if( isAttached(man,&(man->rh),ledges,nledges) ) - { /*if touching ledges*/ - x = man->X + (man->rh).X; - y = man->Y - (man->rh).Y; - if( CheckRopeTouch(x,y,rope,&at)<0 ) /*and touching rope*/ - return(-1); - /*get first piton at (x,y)*/ - /*or index of first available*/ - if( (i = GetPitonIndex(pitons,x,y)) == MX_PITONS ) - return(-1); - - if( PitonToRope(&pitons[i],rope,at,x,y)==-1 ) - return(-1); - } - break; - case 'e': - if( isAttached(man,&(man->lh),ledges,nledges) ) - { - x = man->X - (man->lh).X; - y = man->Y - (man->lh).Y; - if( CheckRopeTouch(x,y,rope,&at)<0 ) /*and touching rope*/ - return(-1); - /*get first piton at (x,y)*/ - /*or index of first available*/ - if( (i = GetPitonIndex(pitons,x,y)) == MX_PITONS ) - return(-1); - - if( PitonToRope(&pitons[i],rope,at,x,y)==-1 ) - return(-1); - } - break; - } - - DangleEnds(rope); - - DisplayMan(man,HOTMAN); - DisplayRope(rope,ROPECOLOR); - DisplayPitons(pitons); - *pitonkey = 0; - - return(0); -} - -PitonToRope(TIEPOINT *piton,ROPE *rope,TIE *at,int x,int y) -{ -int j; - switch( piton->status ) - { - case T_ON_LEDGE: - piton->status = T_ON_LEDGE_SLIDING; - (piton->tie)->status = T_ON_LEDGE_SLIDING; - break; - case T_ON_LEDGE_SLIDING: /*If deleting a piton*/ - DisplayRope(rope,BACKGROUND); - DeleteTie( piton->tie ); - piton->status = T_OFF; - _setcolor(BACKGROUND); - _ellipse(_GFILLINTERIOR,x-2,y-2,x+2,y+2); - _setcolor(SLIDINGCOLOR); /*this fixes a monitor bug*/ - _ellipse(_GFILLINTERIOR,x-2,y-2,x+2,y+2); - _setcolor(BACKGROUND); - _ellipse(_GFILLINTERIOR,x-2,y-2,x+2,y+2); - break; - default: /*If adding a new piton*/ - if( (j = GetFreeIndex(rope))== MX_TIES ) - return(-1); - - DisplayRope(rope,BACKGROUND); - piton->status = T_ON_LEDGE; - piton->x = x; - piton->y = y; - piton->tie = at; - AddTiepoint(at,&(rope->tie[j]),piton,T_ON_LEDGE); - break; - } - return(0); -} - - -InitPitons(TIEPOINT pitons[]) -{ -int i; - for(i=0;itie[0]); - while( tie!=NULL ) - { - printf("\ncounter=%d [pre,tie,post]=[%d,%d,%d] (x,y)=(%d,%d) status=%d", - j,tie->pre,tie,tie->post,*(tie->x),*(tie->y),tie->status); - j++; - tie = tie->post; - } - printf("\n "); - return(0); -} - - \ No newline at end of file diff --git a/CLIFFS1/ATROPE2.C b/CLIFFS1/ATROPE2.C deleted file mode 100644 index 5abe571..0000000 --- a/CLIFFS1/ATROPE2.C +++ /dev/null @@ -1,379 +0,0 @@ -/*++ATROPE2.C -has to do with the kinetics of man pulling on rope. -The 2 main functions are ExecuteRopeMoves() and NewSettleWeight() -which handle (respectively) the man/rope relation and the settling -(on ledge and rope). ---*/ -#include -#include -#include -#include -#include -#include -#include -#include "atdefs.g" - -/************************PULLING PHYSICS*********************************/ - -ExecuteRopeMoves(int *dX, int *dY, MAN *man, BELAY belay[],ROPE *rope,int mindex, int direction,int ledgesupport) -{ -LIMB *limb,*oldlimb; -TIEPOINT *t,*otherbod; - - otherbod= &belay[(mindex+1)%2].b; - - t = &belay[mindex].l; /*FOR THE LEFT HAND*/ - limb = &man->lh; - oldlimb = &man->Olh; - - if( CheckLimbCentering(t,limb,ledgesupport ) ) - LimbRopeMove(limb,oldlimb,&belay[mindex],rope,t,otherbod,dX,dY,direction); - - - t = &belay[mindex].r; /*FOR THE RIGHT HAND*/ - limb = &man->rh; - oldlimb = &man->Orh; - if( CheckLimbCentering(t,limb,ledgesupport ) ) - LimbRopeMove(limb,oldlimb,&belay[mindex],rope,t,otherbod,dX,dY,direction); - - return(0); -} - -CheckLimbCentering(TIEPOINT *t,LIMB *hand,int ledgesupport) -{ - if( t->status!=T_ON_MAN ) - return(0); - if( (ledgesupport==0 && hand->X==0 ) - || ledgesupport>0 ) - return(1); - else - return(0); -} - -LimbRopeMove(LIMB *limb,LIMB *oldlimb,BELAY *belay,ROPE *rope,TIEPOINT *t, - TIEPOINT *otherbod,int *dX,int *dY,int direction) -{ -int tempX=0,tempY=0; -int a,b,lift; -int inpiv,outpiv,inpull,outpull; - - if( t->status!=T_ON_MAN ) /*if belayed to rope and*/ - return(0); - - swingBod(&tempX,&tempY,limb,oldlimb,direction); - - if( tempX || tempY ) /*and arm motion is opposed to direction*/ - { - - inpull = TestPull(rope,belay,otherbod,&a,&b,INROPE,&lift); - inpiv = isPivotForward(a,b,t->x,t->y,direction); - outpull = TestPull(rope,belay,otherbod,&a,&b,OUTROPE,&lift); - outpiv = isPivotForward(a,b,t->x,t->y,direction); - - if( inpiv ) /*if you are pulling on a tiepoint*/ - { - if( inpull ) /*if fixed, move man*/ - { - *dX = tempX; - *dY = tempY; - } - else /*if loose pull in some rope*/ - AdjustRopeEnds(rope,abs(tempX-tempY),END0,-1); - - if( outpull==0 && belay->b.status!=T_ON_MAN ) - AdjustRopeEnds(rope,abs(tempX-tempY),ENDL,1); - } - - if( outpiv ) - { - if( outpull ) - { - *dX = tempX; - *dY = tempY; - } - else - AdjustRopeEnds(rope,abs(tempX-tempY),ENDL,-1); - - if( inpull==0 && belay->b.status!=T_ON_MAN ) - AdjustRopeEnds(rope,abs(tempX-tempY),END0,1); - } - } - return(0); -} - -TestPull(ROPE *rope,BELAY *belay,TIEPOINT *otherbod,int *a,int *b,int dir,int *lift) -{ -int ret = 0; -TIE *ltie,*rtie,*btie,*sie; -int btemp; - ltie = belay->l.tie; - rtie = belay->r.tie; - btie = belay->b.tie; - if( dir==1 ) - sie = &rope->tie[0]; - else - sie = &rope->tie[1]; - - while( sie!=NULL ) - { - if( sie==ltie - || sie==rtie - || sie==btie ) - { - btemp = *(sie->y); - NextTie(&sie,-dir); - *a = *(sie->x); - *b = *(sie->y); - if( *b < btemp ) - *lift = 1; - else - *lift = 0; - break; - } - - if( (sie==otherbod->tie && sie->status==T_ON_MAN) - || sie->status==T_ON_LEDGE ) - ret = 1; - - NextTie(&sie,dir); - } - - if( sie==NULL ) - ret = 0; - - return(ret); -} - -AdjustRopeEnds(ROPE *rope,int length,int end,int sign) -{ - if( end==END0 ) - { - rope->end0 += sign*length; - if( rope->end0 < 10 ) - rope->end0 = 10; - } - else if( end==ENDL ) - { - rope->endL += sign*length; - if( rope->endL < 10 ) - rope->endL = 10; - } - return(0); -} - - -/*************************SETTLING PHYSICS**************************/ - -NewSettleWeight(MAN *man,BELAY belay[],LEDGE ledges[],int N,ROPE *rope,int manindex) -{ -int ledgesupport,inpull,outpull,ilift=0,olift=0,ia,ib,oa,ob; -TIEPOINT *otherbod; - - ledgesupport = LedgeSupport(man,ledges,N); - - if( ledgesupport > 2 ) - return(0); - if( OnBelay(&belay[manindex]) < 2 ) - return( StartFall(man,&belay[manindex],ledges,N,rope) ); - - otherbod = &(belay[(manindex+1)%2].b); - - inpull = TestPull(rope,&belay[manindex],otherbod,&ia,&ib, 1,&ilift); - outpull = TestPull(rope,&belay[manindex],otherbod,&oa,&ob,-1,&olift); - - if( ilift + olift + ledgesupport > 2 ) /*hold a rope supported stance*/ - return(0); - - if( inpull==1 && outpull==0 ) - return( SwingBaby( ia, ib,ilift, olift , man , &belay[manindex],rope,ledges,N) ); - else if( inpull==0 && outpull==1 ) - return( SwingBaby( oa, ob,ilift, olift , man , &belay[manindex],rope,ledges,N) ); - else if( inpull==0 && outpull==0 ) - return( StartFall(man,&belay[manindex],ledges,N,rope) ); - else /* if( inpull==1 && outpull==1 )*/ - { - if( ia<=man->X && man->X<=oa - || oa<=man->X && man->X<=ia ) - return( HangDrop(man,&belay[manindex],otherbod,ledges,N,rope) ); - else if( abs(ia - man->X) < abs(oa - man->X) ) - return( SwingBaby( ia, ib, ilift, olift ,man , &belay[manindex],rope,ledges,N) ); - else - return( SwingBaby( oa, ob, ilift, olift ,man , &belay[manindex],rope,ledges,N) ); - } -} - -NextTie(TIE **tie,int dir) -{ - if( dir==1 ) - *tie = (*tie)->post; - else - *tie = (*tie)->pre; - return(0); -} - -HangDrop(MAN *man, BELAY *belay,TIEPOINT *otherbod,LEDGE ledges[],int N,ROPE *rope) -{ -int ilift=0,olift=0,ia,ib,oa,ob; -int y0,y1; - y1 = y0 = man->Y; - - while( (ilift==0 || olift==0) && (y1-y0)<100 ) - { - man->Y++; - y1++; - UpdateBelay(belay,man); - DangleEnds(rope); - - if( ilift + olift + LedgeSupport(man,ledges,N) > 2 ) - return(0); - - TestPull(rope,belay,otherbod,&ia,&ib, 1,&ilift); - TestPull(rope,belay,otherbod,&oa,&ob,-1,&olift); - } - - if( y1-y0==100 ) - return( -1 ); - else - { - man->X = 10*((man->X+5)/10); /*round to nearest "10" spot*/ - man->Y = 10*((man->Y+5)/10); - return( 0 ); - } -} - -SwingBaby(int a,int b,int ilift, int olift, MAN *man,BELAY *belay,ROPE *rope, - LEDGE ledges[],int N) -{ -int x=man->X,y=man->Y; -int sign,lim; -double u,v,r; - if( xa ) - sign = -1; - else - sign = 0; - - lim = b + 20; - - while( y < lim ) /*fall until the man is a few feet below pivot*/ - { - y+=5; - ReplaceMan(man,rope,belay,x,y); - if( ilift + olift + LedgeSupport(man, ledges, N) > 2 ) - return(0); - } - - u = (double)(x-a); - v = (double)(y-b); - r = sqrt( u*u + v*v ); - - if( sign==0 ) - return(0); - - lim = a+(a-x); /*find mirror position of x rel a*/ - if( sign>0 ) /*shorten the swing slightly*/ - lim -=20; - else - lim +=20; - - while( sign*x < sign*lim ) /*then swing*/ - { - x +=sign*5; - y = Circle(x,a,b,r); - ReplaceMan(man,rope,belay,x,y); - if( ilift + olift + LedgeSupport(man, ledges, N) > 2 ) - return(0); - } - ReplaceMan(man,rope,belay,10*((man->X+5)/10),10*((man->Y+5)/10)); - - return(0); -} - - -ReplaceMan(MAN *man,ROPE *rope,BELAY *belay,int x,int y) -{ - DisplayMan(man,BACKGROUND); /*erase man*/ - DisplayRope(rope,BACKGROUND); - man->X = x; /*change his coords*/ - man->Y = y; - UpdateBelay(belay,man); /*update what depends on him*/ - DangleEnds(rope); /*redisplay stuff*/ - DisplayMan(man,HOTMAN); - DisplayRope(rope,ROPECOLOR); - return(0); -} - -StartFall(MAN *man,BELAY *belay,LEDGE ledges[],int N,ROPE *rope) -{ -int y0,y1; - y1 = y0 = man->Y; - while( (y1-y0)< 100 ) /*you start to fall*/ - { - DisplayMan(man,HOTMAN); - DisplayRope(rope,ROPECOLOR); - if( LedgeSupport(man,ledges,N) > 2 ) - return(0); - else - { - DisplayMan(man,BACKGROUND); - DisplayRope(rope,BACKGROUND); - man->Y += 10; - y1 += 10; - UpdateBelay(belay,man); - DangleEnds(rope); - } - } - return(-1); -} - -OnBelay(BELAY *belay) -{ -int bcount = 0; - if( belay->l.status==T_ON_MAN ) - bcount += 1; - if( belay->r.status==T_ON_MAN ) - bcount += 1; - if( belay->b.status==T_ON_MAN ) /*a sliding body belay doesn't support*/ - bcount += 2; /*a fixed body belay doubly supports*/ - return(bcount); -} - -Circle(int x,int a,int b,double r) -{ -double arg; - arg = r*r-(double)((x-a)*(x-a)); - if( arg>0.0 ) - return( b + (int)sqrt(arg) ); - else - return(b); -} - -isPivotForward(int a, int b , int handX, int handY, int direction) -{ -int ret = 0; - switch(direction) - { - case UP: - if( handY > b ) - ret = 1; - break; - case DOWN: - if( handY < b ) - ret = 1; - break; - case RIGHT: - if( handX < a ) - ret = 1; - break; - case LEFT: - if( handX > a ) - ret = 1; - break; - } - return(ret); -} - - - - \ No newline at end of file diff --git a/CLIFFS1/ATUTILS.C b/CLIFFS1/ATUTILS.C deleted file mode 100644 index a6b8f6c..0000000 --- a/CLIFFS1/ATUTILS.C +++ /dev/null @@ -1,663 +0,0 @@ -/*++ -ATUTILS.C contains fragments and proc's and exec's. ---*/ - -#include -#include -#include -#include -#include -#include -#include "atdefs.g" - -#include "cxldef.h" -#include "sound.g" - -/***********************************PROCS******************************/ -key_proc(U_DATA *keys) -{ -int c=0; - if( kbhit() ) - { - keys->i++; - if( keys->i==MX_KEYS ) - keys->i = 0; - - c = getch(); - if( c==0 ) - c = getch(); - - keys->data[keys->i] = c; - keys->status = HOT; - } - else - keys->status = READY; - - return( keys->status ); -} - - -tm_proc(TM_DATA *tm , double TimeLimit ) -{ -int start,hstart,stop, hstop; -double diff; - - switch( tm->status ) - { - case READY: - case DONE: - break; - case START: - _dos_gettime( &(tm->begin) ); - tm->status = BUSY; - break; - case BUSY: - _dos_gettime( &(tm->end) ); - start = tm->begin.second; - hstart = tm->begin.hsecond; - stop = tm->end.second; - hstop = tm->end.hsecond; - diff = (double)(stop-start) + - (double)(hstop-hstart)/100.0; - if( diff > TimeLimit ) - tm->status = DONE; - break; - } - return(tm->status); -} - - -move_proc(U_DATA *moves, int c) -{ -int i; - if( moves->status!=M_HOT ) - return(0); - - i = moves->i; - moves->data[i] = c; - - i++; - if( i==MX_MOVES ) - i = 0; - - moves->i = i; - moves->status = READY; - return(0); -} - -clock_proc(TM_DATA *clock) -{ -static struct dostime_t temp; -int clickpause=9; -int t; - if( clock->status==START ) - { - _dos_gettime(&clock->begin); - _dos_gettime(&clock->end); - _dos_gettime(&temp); - clock->status = READY; - } - else - { - _dos_gettime(&clock->end); - if( (t=TimeDiff(&clock->end,&temp)) > clickpause ) - { - _dos_gettime(&temp); - clock->status = HOT; - } - else - clock->status = READY; - } - return(0); -} - -/******************************UTILS***************************************/ - -InitMan(MAN *man,int x,int y) -{ - man->X = x; - man->Y = y; - - man->rh.X = MX_HEXT; - man->rh.Y = 0; - man->rh.LR = 1; - man->rh.DU = 1; - - man->lh.X = MX_HEXT; /*limb extensions*/ - man->lh.Y = 0; - man->lh.LR = -1; - man->lh.DU = 1; - - man->rf.X = MX_HEXT; - man->rf.Y = MX_VEXT; - man->rf.LR = 1; - man->rf.DU = -1; - - man->lf.X = MX_HEXT; - man->lf.Y = MX_VEXT; - man->lf.LR = -1; - man->lf.DU = -1; - - CopyAllLimbs(man); - - return(0); -} - -DisplayLedges(LEDGE ledge[],int n) -{ -int i; - _setcolor(LEDGECOLOR); - for( i=0; iX,b=man->Y-5, b2=man->Y; /*subtracted 5 from both y coords*/ - _setcolor(color); - - _moveto(a,b); /*body*/ - _lineto(a,b2); - - _moveto(a,b); /*left hand*/ - _lineto(a + man->lh.LR*man->lh.X, b2 -man->lh.DU*man->lh.Y); - _moveto(a,b); /*right hand*/ - _lineto(a + man->rh.LR*man->rh.X, b2 -man->rh.DU*man->rh.Y); - _moveto(a,b2); /*left foot*/ - _lineto(a + man->lf.LR*man->lf.X, b2 -man->lf.DU*man->lf.Y); - _moveto(a,b2); /*right foot*/ - _lineto(a + man->rf.LR*man->rf.X, b2 -man->rf.DU*man->rf.Y); - - _ellipse(_GBORDER,a-2,b-8,a+2,b-3); /*head*/ - - return(0); -} - -fall(MAN *man) -{ -int j,i; -int start = man->Y; - for(j=start;j<470;j++) - { - for(i=0;i<100;i++) - ; - DisplayMan(man,BACKGROUND); - man->Y++; - DisplayMan(man,HOTMAN); - } - -_settextcolor(HOTTEXT); -_settextposition(1,1); -_outtext("Your climbing companion watches in shocked horror"); -_settextposition(2,1); -_outtext("as your body slides down a short icefield, then "); -_settextposition(3,1); -_outtext("cartwheels out over the 3,000 foot abyss..."); -getch(); - return(0); -} - -Congratulations() -{ - _settextcolor(HOTTEXT); - _settextposition(1,1); - _outtext("It is amazing! You made it to the top!"); - _settextposition(2,1); - _outtext("The view of the surrounding peaks is spectacular"); - _settextposition(3,1); - _outtext("Although tired, you feel an overwhelming sense of joy."); - _settextposition(4,1); - _outtext("You hardly notice the frostbite beginning to numb your hands."); - getch(); - - _clearscreen(_GCLEARSCREEN); - putpik("ev6",120,0); - getch(); - _settextposition(1,1); - _outtext(" "); - _settextposition(1,1); - _outtext(" "); - - return(0); -} - -Retreat() -{ -_settextcolor(HOTTEXT); -_settextposition(1,1); -_outtext("You decide to retreat for today but"); -_settextposition(2,1); -_outtext("are happy knowing you'll be back."); -getch(); - return(0); -} - -int GameInstruct(void) -{ - _settextcolor(WHITE); - _clearscreen(_GCLEARSCREEN); - _settextposition(1,1); -_outtext(" ************ ATTACK THE CLIFFS ************ \n"); -_outtext(" *********** QUICK REFERENCE ***********\n"); -_outtext(" \n"); -_outtext(" CopyRite(C) 1992 Peter Waksman\n"); -_outtext(" \n"); -_outtext(" quit\n"); -_outtext(" select PeterB \n"); -_outtext(" select DougalH\n"); -_outtext(" select direction\n"); -_outtext("\n"); -_outtext(" To move use:\n"); -_outtext("\n"); -_outtext(" left right

\n"); -_outtext(" hand hand \n"); -_outtext(" left right \n"); -_outtext(" foot foot \n"); -_outtext("\n"); -_outtext("\n --HIT ANY KEY--\n"); -_outtext("\n"); - tune(); -getch(); -_outtext(" To belay rope to man use:\n"); -_outtext("\n"); -_outtext(" left right \n"); -_outtext(" hand hand\n"); -_outtext(" body \n"); -_outtext(" \n"); -_outtext(" To belay rope to ledge use:\n"); -_outtext("\n"); -_outtext(" left right \n"); -_outtext(" hand hand\n"); -_outtext("\n"); -_outtext(" Note1: To be supported the man must have either the rope attached\n"); -_outtext(" or a leg and some other limb. For example 2 arms and no rope will\n"); -_outtext(" not support.\n"); -_outtext("\n"); -_outtext(" Note2: The color of a belay dot determines if the attachment is a \n"); -_outtext(" fixed knot or a sliding one: yellow=fixed blue=sliding.\n"); -_outtext("\n"); -_outtext(" Note3: You can only climb up the rope when your arms are centered.\n"); -_outtext(" \n"); -_outtext(" Note4: You can hit movement keys simultaneously for moving limbs\n"); -_outtext(" simultaneously; also you can hit keys quickly in sucession to make\n"); -_outtext(" complex movements.\n"); -_outtext(" \n"); -_outtext("\n --HIT ANY KEY--\n"); - getch(); - - _clearscreen(_GCLEARSCREEN); - _settextposition(10,20); - printf("Use to continue"); - _settextposition(12,20); - printf("Use to quit (a wise decision)"); - - return(0); - -} - - -BelayKey(int manflag) -{ - if( manflag==1) - { - _settextcolor(HOTTEXT); - _settextposition(29,15); - _outtext("DougalH:"); - _settextcolor(COLDTEXT); - _settextposition(29,1); - _outtext("PeterB:"); - } - else if( manflag==2 ) - { - _settextcolor(COLDTEXT); - _settextposition(29,15); - _outtext("DougalH:"); - _settextcolor(HOTTEXT); - _settextposition(29,1); - _outtext("PeterB:"); - } - - return(0); -} - -int MiniKey(void) -{ -int start = 23; - _settextcolor(HOTTEXT); - - _settextposition(start,1); - _outtext(""); - _settextposition(start+1,2); - _outtext(""); - _settextposition(start+3,1); - _outtext(""); - _settextposition(start+4,2); - _outtext(""); - - - _settextposition(start,7); - _outtext("

"); - _settextposition(start+1,6); - _outtext(""); - _settextposition(start+3,7); - _outtext(""); - _settextposition(start+4,6); - _outtext(""); - - return(0); -} - -FlipDirection(int *direction, int c) -{ -int Ox=535,Oy=445; - switch( c ) - { - case 75: - DisplayArrow(Ox,Oy,*direction,BACKGROUND); - *direction = LEFT; - DisplayArrow(Ox,Oy,LEFT,ARROWCOLOR); - break; - case 77: - DisplayArrow(Ox,Oy,*direction,BACKGROUND); - *direction = RIGHT; - DisplayArrow(Ox,Oy,RIGHT,ARROWCOLOR); - break; - case 72: - DisplayArrow(Ox,Oy,*direction,BACKGROUND); - *direction = UP; - DisplayArrow(Ox,Oy,UP,ARROWCOLOR); - break; - case 80: - DisplayArrow(Ox,Oy,*direction,BACKGROUND); - *direction = DOWN; - DisplayArrow(Ox,Oy,DOWN,ARROWCOLOR); - break; - } - return(0); -} - -DisplayArrow(int x, int y, int direction,int color) -{ -int tipX,tipY; - _setcolor(color); - switch( direction ) - { - case UP: - tipX = x + 10; - tipY = y + 3; - _moveto(tipX,tipY); /*the shaft*/ - _lineto(tipX,y+20); - _moveto(tipX+1,tipY); - _lineto(tipX+1,y+20); - - _moveto(tipX,tipY); - _lineto(x+3,y+10); - - _moveto(tipX,tipY); - _lineto(x+17,y+10); - - break; - case DOWN: - tipX = x + 10; - tipY = y + 17; - _moveto(tipX,tipY); /*the shaft*/ - _lineto(tipX,y); - _moveto(tipX+1,tipY); - _lineto(tipX+1,y); - - _moveto(tipX,tipY); - _lineto(x+3,y+10); - - _moveto(tipX,tipY); - _lineto(x+17,y+10); - - break; - case LEFT: - tipX = x + 3; - tipY = y + 10; - _moveto(tipX,tipY); /*the shaft*/ - _lineto(x+20,tipY); - _moveto(tipX,tipY+1); - _lineto(x+20,tipY+1); - - _moveto(tipX,tipY); - _lineto(x+10,y+3); - - _moveto(tipX,tipY); - _lineto(x+10,y+17); - - break; - case RIGHT: - tipX = x + 17; - tipY = y + 10; - _moveto(tipX,tipY); /*the shaft*/ - _lineto(x,tipY); - _moveto(tipX,tipY+1); - _lineto(x,tipY+1); - - _moveto(tipX,tipY); - _lineto(x+10,y+3); - - _moveto(tipX,tipY); - _lineto(x+10,y+17); - - break; - } - return(0); -} - -DisplayBelayStatus(BELAY *belay, int Ox, int Oy) -{ -int x = Ox; -int y = Oy; -int u; - - if( belay->l.status==T_OFF ) - { - _setcolor(BACKGROUND); - u = _GFILLINTERIOR; - _ellipse(u,x-2,y-2,x+2,y+2); - _setcolor(YELLOW); - u = _GBORDER; - _ellipse(u,x-2,y-2,x+2,y+2); - } - else - { - _setcolor(FIXEDCOLOR); - u = _GFILLINTERIOR; - _ellipse(u,x-2,y-2,x+2,y+2); - } - - x += 7; - if( belay->b.status==T_OFF ) - { - _setcolor(BACKGROUND); - u = _GFILLINTERIOR; - _ellipse(u,x-2,y-2,x+2,y+2); - _setcolor(YELLOW); - u = _GBORDER; - _ellipse(u,x-2,y-2,x+2,y+2); - } - else if( belay->b.status==T_ON_MAN_SLIDING ) - { - u = _GFILLINTERIOR; - _setcolor(SLIDINGCOLOR); - _ellipse(u,x-2,y-2,x+2,y+2); - } - else if( belay->b.status==T_ON_MAN ) - { - u = _GFILLINTERIOR; - _setcolor(FIXEDCOLOR); - _ellipse(u,x-2,y-2,x+2,y+2); - } - - x += 7; - if( belay->r.status==T_OFF ) - { - _setcolor(BACKGROUND); - u = _GFILLINTERIOR; - _ellipse(u,x-2,y-2,x+2,y+2); - _setcolor(YELLOW); - u = _GBORDER; - _ellipse(u,x-2,y-2,x+2,y+2); - } - else - { - u = _GFILLINTERIOR; - _setcolor(FIXEDCOLOR); - _ellipse(u,x-2,y-2,x+2,y+2); - } - - return(0); -} - -EraseScene(MAN *man,ROPE *rope) -{ - DisplayMan(man,BACKGROUND); - DisplayRope(rope,BACKGROUND); - return(0); -} - -ReDisplayScene(int manindex,MAN man[],ROPE *rope,LEDGE ledges[],int nledges) -{ - DisplayLedges(ledges,nledges); - DisplayMan(&man[manindex],HOTMAN); - DisplayMan(&man[(manindex+1)%2],COLDMAN); - DisplayRope(rope,ROPECOLOR); - return(0); -} - -DisplayMoveCount(int count) -{ -char string1[30],string2[30]; - _settextposition(29,40); - _settextcolor(HOTTEXT); - strcpy(string1,"MOVE = "); - itoa(count,string2,10); - strcat(string1,string2); - _outtext(string1); - return(0); -} - - -TimeDiff(struct dostime_t *new,struct dostime_t *old) -{ -int newt = (int)new->second + 60*(int)new->minute; -int oldt = (int)old->second + 60*(int)old->minute; - return(newt-oldt); /*returns the time difference in seconds*/ -} - -DisplayClock(int time) -{ -char string[24]; -int minute,second; - minute = time/60; - second = time-minute*60; - _settextcolor(YELLOW); - _settextposition(29,56); - itoa(minute,string,10); - _outtext(string); - _outtext(":"); - itoa(second,string,10); - _outtext(string); - _outtext(" "); - return(0); -} - -tune() -{ - -int quarter=10; -int eighth = 5; - -if(kbhit() ) -return(0); - sound_(2*D,eighth); /*start at low D*/ - -if(kbhit() ) -return(0); - sound_(G,quarter); - sound_(A,quarter); - sound_(B,eighth); - sound_(D,eighth); - sound_(E,eighth); - sound_(HI_G,eighth); - - - -if(kbhit() ) -return(0); - - sound_(D,quarter); - sound_(B,eighth); - sound_(C,eighth); - sound_(D,quarter); - sound_(B,eighth); - sound_(D,eighth); -if(kbhit() ) -return(0); - sound_(E,quarter); - sound_(B,quarter); - sound_(D,3*quarter); -if(kbhit() ) -return(0); - sound_(REST,2*quarter+eighth); -//sound_(2*G,eighth); -//sound_(REST,quarter); -//sound_(G,eighth); - -//sound_(2*G,eighth); -//sound_(REST,eighth); -//sound_(2*FSHARP,eighth); - sound_(B,eighth); -if(kbhit() ) -return(0); - sound_(E,quarter); - sound_(DSHARP,quarter); - sound_(B,eighth); - sound_(GSHARP,eighth); - sound_(A,eighth); - sound_(CSHARP,eighth); -if(kbhit() ) -return(0); - sound_(E,quarter); - sound_(HI_GSHARP,quarter); - sound_(B,quarter); - sound_(REST,eighth); - sound_(E,eighth); -if(kbhit() ) -return(0); - sound_(HI_FSHARP,2*quarter); - sound_(E,quarter); - sound_(REST,quarter); -if(kbhit() ) -return(0); - sound_(HI_FSHARP,2*quarter+eighth); - sound_(E,eighth); - sound_(C,eighth); - sound_(FSHARP,eighth); - sound_(G,quarter); -if(kbhit() ) -return(0); - - - sound_(4*G,eighth); - sound_(2*D,eighth); - sound_(G,eighth); - sound_(B,quarter); - sound_(HI_G,4*quarter); - - - return(0); -} - - - - - \ No newline at end of file diff --git a/CLIFFS1/ATVIEW.C b/CLIFFS1/ATVIEW.C deleted file mode 100644 index ec55d81..0000000 --- a/CLIFFS1/ATVIEW.C +++ /dev/null @@ -1,283 +0,0 @@ -/*++ATVIEW.C is the file with "eye of the climber" displays ---*/ - -#include -#include -#include -#include -#include -#include "atdefs.g" - - -#define EXT MX_HEXT -#define HEXT 20.0 /* 2*EXT as a double*/ -#define VIEW 150.0 -#define HORIZON 300.0 - -int Perspective(int x,int y,int *a, int *b); - - -/*In displaying each ledge the (x,y) coordinates of the ledges are -re-mapped into (a,b) coordinates. The basic formulas (which get applied -to the ledge endpoints) are: - a = a(x,y) = a0 + (x-manX)*f(y) - b = b(x,y) = b0 + (y-manY)*g(x) -where (a0,b0) is the place the man's center will map to. The functions f(y) -and g(x) are functions varying with the movement direction. - The display is clipped to within a pre-selected window (a1,b1)-(a2,b2). -*/ - -I_DisplayLedges(LEDGE ledges[],int N,MAN *man,int direction, - int a1,int b1, int a2, int b2,int color) -{ -int i; -int a0,b0,a,b; -int x,y; -int manX=man->X,manY=man->Y; - - _setcolor(BACKGROUND); /*erase window*/ - _rectangle(_GFILLINTERIOR,a1,b1,a2,b2); - _setcolor(GREEN); - _rectangle(_GBORDER,a1-2,b1-2,a2+2,b2+2); - - _setcolor(color); - _setcliprgn(a1,b1,a2,b2); - - switch( direction ) - { - case UP: - a0 = (a1+a2)/2; - b0 = b2-2; - for(i=0;irh.X; - y = -man->rh.Y; - Perspective(-y,x,&b,&a); - _moveto(a0+a,b0-b); - _lineto(a0,b0-5); - - x = man->rf.X; - y = man->rf.Y; - Perspective(-y,x,&b,&a); - _moveto(a0+a,b0-b); - _lineto(a0,b0); - - x = -man->lh.X; - y = -man->lh.Y; - Perspective(-y,x,&b,&a); - _moveto(a0+a,b0-b); - _lineto(a0,b0-5); - - x = -man->lf.X; - y = man->lf.Y; - Perspective(-y,x,&b,&a); - _moveto(a0+a,b0-b); - _lineto(a0,b0); - - break; - case DOWN: - a0 = (a1+a2)/2; - b0 = b1+2; - for(i=0;irh.X; - y = -man->rh.Y; - Perspective(y,x,&b,&a); - _moveto(a0+a,b0+b); - _lineto(a0,b0-5); - - x = man->rf.X; - y = man->rf.Y; - Perspective(y,x,&b,&a); - _moveto(a0+a,b0+b); - _lineto(a0,b0); - - x = -man->lh.X; - y = -man->lh.Y; - Perspective(y,x,&b,&a); - _moveto(a0+a,b0+b); - _lineto(a0,b0-5); - - x = -man->lf.X; - y = man->lf.Y; - Perspective(y,x,&b,&a); - _moveto(a0+a,b0+b); - _lineto(a0,b0); - - break; - case LEFT: - b0 = (b1+b2)/2; - a0 = a2-2; - for(i=0;irh.X; - y = -man->rh.Y; - Perspective(-x,y,&a,&b); - _moveto(a0-a,b0+b); - _lineto(a0,b0-5); - - x = man->rf.X; - y = man->rf.Y; - Perspective(-x,y,&a,&b); - _moveto(a0-a,b0+b); - _lineto(a0,b0); - - x = -man->lh.X; - y = -man->lh.Y; - Perspective(-x,y,&a,&b); - _moveto(a0-a,b0+b); - _lineto(a0,b0-5); - - x = -man->lf.X; - y = man->lf.Y; - Perspective(-x,y,&a,&b); - _moveto(a0-a,b0+b); - _lineto(a0,b0); - - break; - case RIGHT: - b0 = (b1+b2)/2; - a0 = a1+2; - for(i=0;irh.X; - y = -man->rh.Y; - Perspective(x,y,&a,&b); - _moveto(a0+a,b0+b); - _lineto(a0,b0-5); - - x = man->rf.X; - y = man->rf.Y; - Perspective(x,y,&a,&b); - _moveto(a0+a,b0+b); - _lineto(a0,b0); - - x = -man->lh.X; - y = -man->lh.Y; - Perspective(x,y,&a,&b); - _moveto(a0+a,b0+b); - _lineto(a0,b0-5); - - x = -man->lf.X; - y = man->lf.Y; - Perspective(x,y,&a,&b); - _moveto(a0+a,b0+b); - _lineto(a0,b0); - - break; - } -// _setcolor(YELLOW); -// _ellipse(_GFILLINTERIOR,a0-2,b0-2,a0+2,b0+2); - - _setcliprgn(0,0,639,479); - return(0); -} - - -Perspective(int x,int y,int *a, int *b) -{ -double f,t; - if( x< -EXT ) - x = -EXT; - t = (double)x; - f = 1.0/(HEXT+t); - - *a = (int)(t*f*VIEW); - *b = (int)((double)y*f*HEXT); - - return(0); -} - -/* -Perspective(int x,int y,int *a, int *b) -{ -double t=(double)x,s; - if( t>HORIZON ) - t = HORIZON; - s = 1.0 - t/(2.0*HORIZON); - *a = (int)(t*s); - *b = (int)((double)y*s); - return(0); -} -*/ - \ No newline at end of file diff --git a/CLIFFS1/CXLDEF.H b/CLIFFS1/CXLDEF.H deleted file mode 100644 index f8718f9..0000000 --- a/CLIFFS1/CXLDEF.H +++ /dev/null @@ -1,115 +0,0 @@ -/* - ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ - ³ ³ - ³ CXLDEF.H - CXL is Copyright (c) 1987-1989 by Mike Smedley. ³ - ³ ³ - ³ This header file contains miscellaneous function prototypes and ³ - ³ definitions. ³ - ³ ³ - ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ -*/ - -#if defined(__TURBOC__) /* Turbo C */ - #if __STDC__ - #define _Cdecl - #else - #define _Cdecl cdecl - #endif - #define _Near -#elif defined(__ZTC__) /* Zortech C++ */ - #define _Cdecl - #define _Near -#elif defined(M_I86) && !defined(__ZTC__) /* Microsoft C/QuickC */ - #if !defined(NO_EXT_KEYS) - #define _Cdecl cdecl - #define _Near near - #else - #define _Cdecl - #define _Near - #endif -#endif - -/*---------------------------[ function prototypes ]-------------------------*/ - -char *_Cdecl biosver(void); -int _Cdecl clockcal(void); -char *_Cdecl cxlver(void); -void _Cdecl delay_(unsigned duration); -unsigned _Cdecl expmem(void); -unsigned _Cdecl extmem(void); -int _Cdecl fcrypt(char *file,char *key); -int _Cdecl gameport(void); -int _Cdecl machid(void); -int _Cdecl mathchip(void); -int _Cdecl numflop(void); -int _Cdecl numpar(void); -int _Cdecl numser(void); -char *_Cdecl randfile(void); -void _Cdecl sound_(unsigned pitch,unsigned duration); -void _Cdecl _stdoutch(int ch); -char *_Cdecl sysdate(int dtype); -char *_Cdecl systime(int ttype); -int _Cdecl tabstop(int col,int tabwidth); -unsigned long _Cdecl timer(void); - -/*-----------------[ definitions for common control codes ]------------------*/ - -#define NUL 0 -#define BEL 7 -#define BS 8 -#define HT 9 -#define LF 10 -#define FF 12 -#define CR 13 -#define ESC 27 - -/*------------------------[ definition of NULL ]-----------------------------*/ - -#if !defined(NULL) - #if defined(__TURBOC__) /* Turbo C */ - #if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) - #define NULL 0 - #else - #define NULL 0L - #endif - #elif defined(__ZTC__) /* Zortech C++ */ - #ifdef LPTR - #define NULL 0L - #else - #define NULL 0 - #endif - #elif defined(M_I86) && !defined(__ZTC__) /* Microsoft C/QuickC */ - #if defined(M_I86SM) || defined(M_I86MM) - #define NULL 0 - #else - #define NULL 0L - #endif - #endif -#endif - -/*-----------------------[ Boolean logic conditions ]------------------------*/ - -#define NO 0 -#define YES 1 -#define FALSE 0 -#define TRUE 1 -#define OFF 0 -#define ON 1 - -/*----------------[ machine ID codes returned from machid() ]----------------*/ - -#define IBMPC 255 /* IBM PC */ -#define IBMPCXT 254 /* IBM PC/XT */ -#define IBMPCJR 253 /* IBM PCjr */ -#define IBMPCAT 252 /* IBM PC/AT */ -#define IBMPCXT2 251 /* IBM PC/XT */ -#define IBMCONV 249 /* IBM PC Convertible */ -#define SPERRYPC 48 /* Sperry PC */ - -/*-----------------------[ macro-function definitions ]-----------------------*/ - -#if !defined(MK_FP) -#define MK_FP(seg,ofs) ((void far *) (((unsigned long)(seg) << 16) | \ - (unsigned)(ofs))) -#endif -#define beep() _stdoutch(BEL) diff --git a/CLIFFS1/DAVE b/CLIFFS1/DAVE deleted file mode 100644 index d75e0bd..0000000 --- a/CLIFFS1/DAVE +++ /dev/null @@ -1 +0,0 @@ -fsdfsdfdfsdfdsfsdcvbcbc diff --git a/CLIFFS1/ED.C b/CLIFFS1/ED.C deleted file mode 100644 index 9badfbe..0000000 --- a/CLIFFS1/ED.C +++ /dev/null @@ -1,208 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "atdefs.g" - -int main(void); -int EditInstruct(void); -int DisplayLedge(LEDGE *ledge, int color); -int EditLedges(LEDGE ledges[],int nledges); - -main() -{ -FILE *fp; -LEDGE *ledges; -int nledges,nledges0,i; -char string[20]; -int c,d; - - ledges = (LEDGE *)malloc(6*MX_LEDGES); - - _setvideomode(_VRES16COLOR); - _settextposition(4,1); - _outtext("Edit New or Old File ?"); - d = getch(); - _outtext("\nEnter name of ledge file:"); - scanf("%s",string); - - if( d=='o' ) - nledges0 = ReadLedges(ledges,string); - else - nledges0 = 0; - - nledges = EditLedges(ledges,nledges0); - - _clearscreen(_GCLEARSCREEN); - _settextposition(5,1); - _outtext("Save File ?"); - c = getch(); - if( c=='y' ) - { - fp = fopen( string, "w" ); -if( d=='o' ) - for(i=0;i=0;i--) - { - itoa( ledges[i].height , string , 10); - fprintf( fp , "%s ," , string ); - itoa( ledges[i].leftend , string , 10); - fprintf( fp , "%s ," , string ); - itoa( ledges[i].rightend , string , 10); - fprintf( fp , "%s ," , string ); - fprintf( fp , "\n"); - } - fclose( fp ); - } - _setvideomode(_DEFAULTMODE); - - return(0); -} - - - -EditLedges(LEDGE ledges[],int nledges) -{ -int i,j; -int c; -int nled = nledges; -int flag = 'm'; - _clearscreen(_GCLEARSCREEN); - EditInstruct(); - - if( nled==0 ) - { - nled = 1; - ledges[0].height = 270; - ledges[0].leftend = 20; - ledges[0].rightend = 600; - } - - i = 0; - DisplayLedges(ledges,nled); - DisplayLedge(&ledges[i],14); - - while( (c=getkey()) != '' ) - { - switch( c ) - { - case 'n': - i++; - if( i==nled ) - i = 0; - flag = 'm'; - break; - case 'm': - flag = 'm'; - break; - case 's': - flag = 's'; - break; - case 'a': - if( nled < MX_LEDGES ) - { - nled++; - i=nled-1; - ledges[i].height = 270; - ledges[i].leftend = 260; - ledges[i].rightend =280; - DisplayLedge(&ledges[i], 8 ); - } - else - printf("\b\a"); /*beep*/ - flag = 'm'; - break; - case 75: /*left*/ - DisplayLedge(&ledges[i], 0 ); - if( flag=='m' ) - { - if( ledges[i].leftend - 10 > 0 ) - { - ledges[i].leftend -= 10; - ledges[i].rightend -= 10; - } - } - else - { - if( ledges[i].leftend + 10 < ledges[i].rightend ) - ledges[i].rightend -= 10; - } - break; - case 77: /*right*/ - DisplayLedge(&ledges[i], 0 ); - if( flag=='m' ) - { - if( ledges[i].rightend + 10 < 640) - { - ledges[i].leftend += 10; - ledges[i].rightend += 10; - } - } - else - if( ledges[i].rightend + 10 < 640) - ledges[i].rightend += 10; - break; - case 72: /*up*/ - DisplayLedge(&ledges[i], 0 ); - if( flag=='m' ) - if( ledges[i].leftend - 10 > 0 ) - ledges[i].height -= 10; - break; - case 80: /*down*/ - DisplayLedge(&ledges[i], 0 ); - if( flag=='m' ) - if( ledges[i].leftend + 10 < 480 ) - ledges[i].height += 10; - break; - default: - flag = 'm'; - break; - } /*ends switch*/ - DisplayLedges(ledges,nled); - DisplayLedge(&ledges[i],14); - } - - - return(nled); -} - -int EditInstruct(void) -{ - _settextposition(1,1); - _outtext("=next ledge"); - _outtext("\n=move ledge"); - _outtext("\n=size ledge"); - _outtext("\n= add ledge"); - _outtext("\nUse arrow keys"); -} - -int getkey(void) -{ -int c = getch(); - if( c==0 ) - c = getch(); - return(c); -} - -int DisplayLedge(LEDGE *ledge, int color) -{ - _setcolor(color); - _moveto(ledge->leftend,ledge->height); - _lineto(ledge->rightend,ledge->height); - _moveto(ledge->leftend,ledge->height-1); - _lineto(ledge->rightend,ledge->height-1); - return(0); -} - \ No newline at end of file diff --git a/CLIFFS1/ED.PIF b/CLIFFS1/ED.PIF deleted file mode 100644 index 90db405..0000000 Binary files a/CLIFFS1/ED.PIF and /dev/null differ diff --git a/CLIFFS1/LK.BAT b/CLIFFS1/LK.BAT deleted file mode 100755 index 0711308..0000000 --- a/CLIFFS1/LK.BAT +++ /dev/null @@ -1,2 +0,0 @@ -link %1+atutils+atmove+atrope+atrope2+read+putpik,,%1.exe,cxlmss.lib; - \ No newline at end of file diff --git a/CLIFFS1/MK.BAT b/CLIFFS1/MK.BAT deleted file mode 100755 index 3cca55c..0000000 --- a/CLIFFS1/MK.BAT +++ /dev/null @@ -1,3 +0,0 @@ -cl %1.c /c -link %1+atutils+atmove+atrope+atrope2+read+putpik,,%1.exe,cxlmss.lib; - \ No newline at end of file diff --git a/CLIFFS1/PUTPIK.C b/CLIFFS1/PUTPIK.C deleted file mode 100644 index 6ead83c..0000000 --- a/CLIFFS1/PUTPIK.C +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include -#include -#include - -putpik(char string[],int x,int y) -{ -int i,j; -unsigned int x1,y1,x2,y2; -FILE *fp; -int high,wide; -int val,val1,val2; - - strcat( string, ".pik"); - fp = fopen(string,"r"); - - x1 = x; - y1 = y; - - x2 = x1 + 400; - y2 = y1 + 400; - - for(i=x1;i -#include -#include -#include - -#include "newdefs.g" - - /*reads the file named "ledges"*/ - /*returns the number of ledges*/ -int ReadLedges(LEDGE ledges[],char filename[]); - -int ReadLedges(LEDGE ledges[], char filename[]) -{ -FILE *fp; -char string[120],temp[20]; -int i = 0; -int k,top; -int c; - - - fp = fopen(filename,"r"); - - do - { - if( NULL == fgets( string,120, fp ) ) - break; - - /*get ledge height*/ - for(k=0;k<20;k++) - { - if( (c=(int)string[k])==',' ) - break; - else - temp[k] = string[k]; - } - top = k+1; - temp[k+1] = 0; - - ledges[i].height = atoi( temp ); - - for(k=0;k<20;k++) /*get ledge left end*/ - { - if( (c=(int)string[k+top])==',' ) - break; - else - temp[k] = string[k+top]; - } - top = k+1+top; - temp[k+1] = 0; - - ledges[i].leftend = atoi( temp ); - - for(k=0;k<20;k++) /*get ledge right end*/ - { - if( (c=(int)string[k+top])==',' ) - break; - else - temp[k] = string[k+top]; - } - top = k+1+top; - temp[k+1] = 0; - - ledges[i].rightend = atoi( temp ); - -/*printf("\ni=%d height=%d left=%d right=%d",i,ledges[i].height, ledges[i].leftend, ledges[i].rightend);*/ - - i++; - - }while( i +#include +#include + +enum { + screen_width = 640, + screen_height = 480, + mx_ties = 20, + mx_pitons = 20, + mx_moves = 16, + mx_ledges = 500 +}; + +typedef enum { + dir_up = 1, + dir_down = 2, + dir_left = 3, + dir_right = 4 +} Direction; + +typedef enum { + tie_off = 0, + tie_on_man = 1, + tie_on_man_sliding = 2, + tie_on_ledge = 3, + tie_on_ledge_sliding = 4 +} TieStatus; + +typedef enum { + result_none = 0, + result_fall = -1, + result_win = 1, + result_retreat = 2 +} RoundResult; + +typedef enum { + key_none = 0, + key_arrow_up = 1001, + key_arrow_down, + key_arrow_left, + key_arrow_right, + key_f1, + key_f2, + key_enter, + key_escape +} KeyCode; + +typedef struct { + int lr; + int du; + int x; + int y; +} Limb; + +typedef struct { + int x; + int y; + Limb lh; + Limb rh; + Limb lf; + Limb rf; + Limb old_lh; + Limb old_rh; + Limb old_lf; + Limb old_rf; +} Man; + +typedef struct { + int height; + int left_end; + int right_end; +} Ledge; + +typedef struct Tie { + int status; + int *x; + int *y; + struct Tie *pre; + struct Tie *post; +} Tie; + +typedef struct { + int x0; + int y0; + int xl; + int yl; + int end0; + int endl; + Tie ties[mx_ties]; +} Rope; + +typedef struct { + int status; + int x; + int y; + Tie *tie; +} TiePoint; + +typedef struct { + TiePoint l; + TiePoint b; + TiePoint r; +} Belay; + +typedef struct { + char path[512]; + Ledge ledges[mx_ledges]; + int count; +} Level; + +typedef struct Game Game; + +typedef struct { + void (*present)(void *userdata, const Game *game); + void (*delay)(void *userdata, uint32_t ms); + void *userdata; +} RenderHooks; + +struct Game { + Level level; + Rope rope; + TiePoint pitons[mx_pitons]; + Man men[2]; + Belay belays[2]; + int active_man; + int direction; + int move_count; + int pending_man; + int pending_belay_key; + int pending_piton_key; + int move_queue[mx_moves]; + int queued_moves; + bool move_batch_active; + uint64_t move_batch_started_ms; + uint64_t started_ms; + RoundResult result; + bool round_over; + RenderHooks hooks; +}; + +bool resolve_level_path(const char *requested, char *out, size_t out_size); +bool game_init( + Game *game, + const char *level_name, + const RenderHooks *hooks, + uint64_t now_ms, + char *error, + size_t error_size +); +void game_press_key(Game *game, int key, uint64_t now_ms); +void game_update(Game *game, uint64_t now_ms); +int game_elapsed_seconds(const Game *game, uint64_t now_ms); +const char *game_result_title(RoundResult result); +const char *game_result_line(RoundResult result, int line); + +#endif diff --git a/src/game.c b/src/game.c new file mode 100644 index 0000000..4bf5045 --- /dev/null +++ b/src/game.c @@ -0,0 +1,1351 @@ +#include "cliffs.h" + +#include +#include +#include +#include +#include +#include + +enum { + max_vertical_extension = 20, + max_horizontal_extension = 10, + limb_step = 10, + active_man_dougal = 0, + active_man_peter = 1 +}; + +static bool file_exists(const char *path) +{ + FILE *fp = fopen(path, "rb"); + + if (fp == NULL) { + return false; + } + + fclose(fp); + return true; +} + +static const char *basename_ptr(const char *path) +{ + const char *slash = strrchr(path, '/'); + const char *backslash = strrchr(path, '\\'); + const char *base = path; + + if (slash != NULL && slash + 1 > base) { + base = slash + 1; + } + if (backslash != NULL && backslash + 1 > base) { + base = backslash + 1; + } + + return base; +} + +static bool has_extension(const char *path) +{ + return strchr(basename_ptr(path), '.') != NULL; +} + +static void add_candidate(char candidates[][512], int *count, const char *value) +{ + int i; + + for (i = 0; i < *count; ++i) { + if (strcmp(candidates[i], value) == 0) { + return; + } + } + + if (*count >= 8) { + return; + } + + snprintf(candidates[*count], 512, "%s", value); + (*count)++; +} + +bool resolve_level_path(const char *requested, char *out, size_t out_size) +{ + char candidates[8][512]; + int count = 0; + char buffer[512]; + const char *name = (requested != NULL && *requested != '\0') ? requested : "ledges"; + const char *base = basename_ptr(name); + int i; + + add_candidate(candidates, &count, name); + + if (!has_extension(name)) { + snprintf(buffer, sizeof(buffer), "%s.ldg", name); + add_candidate(candidates, &count, buffer); + } + + snprintf(buffer, sizeof(buffer), "levels/%s", base); + add_candidate(candidates, &count, buffer); + + if (!has_extension(base)) { + snprintf(buffer, sizeof(buffer), "levels/%s.ldg", base); + add_candidate(candidates, &count, buffer); + } + + for (i = 0; i < count; ++i) { + if (file_exists(candidates[i])) { + snprintf(out, out_size, "%s", candidates[i]); + return true; + } + } + + return false; +} + +static bool load_level(Level *level, const char *requested, char *error, size_t error_size) +{ + char resolved[512]; + FILE *fp; + char line[256]; + + if (!resolve_level_path(requested, resolved, sizeof(resolved))) { + snprintf(error, error_size, "could not find level file '%s'", requested != NULL ? requested : "ledges"); + return false; + } + + fp = fopen(resolved, "rb"); + if (fp == NULL) { + snprintf(error, error_size, "failed to open '%s': %s", resolved, strerror(errno)); + return false; + } + + memset(level, 0, sizeof(*level)); + snprintf(level->path, sizeof(level->path), "%s", resolved); + + while (fgets(line, sizeof(line), fp) != NULL) { + char *cursor = line; + char *end = NULL; + long values[3]; + int parsed = 0; + + while (*cursor != '\0' && parsed < 3) { + long value = strtol(cursor, &end, 10); + if (end != cursor) { + values[parsed++] = value; + cursor = end; + } else { + cursor++; + } + } + + if (parsed == 3 && level->count < mx_ledges) { + level->ledges[level->count].height = (int)values[0]; + level->ledges[level->count].left_end = (int)values[1]; + level->ledges[level->count].right_end = (int)values[2]; + level->count++; + } + } + + fclose(fp); + + if (level->count == 0) { + snprintf(error, error_size, "no ledges found in '%s'", resolved); + return false; + } + + return true; +} + +static void animate(Game *game, uint32_t delay_ms) +{ + if (game->hooks.present != NULL) { + game->hooks.present(game->hooks.userdata, game); + } + if (delay_ms > 0 && game->hooks.delay != NULL) { + game->hooks.delay(game->hooks.userdata, delay_ms); + } +} + +static Man *current_man(Game *game) +{ + return &game->men[game->active_man]; +} + +static void copy_limb(Limb *dest, const Limb *source) +{ + dest->lr = source->lr; + dest->du = source->du; + dest->x = source->x; + dest->y = source->y; +} + +static void copy_all_limbs(Man *man) +{ + copy_limb(&man->old_rh, &man->rh); + copy_limb(&man->old_lh, &man->lh); + copy_limb(&man->old_lf, &man->lf); + copy_limb(&man->old_rf, &man->rf); +} + +static void init_man(Man *man, int x, int y) +{ + memset(man, 0, sizeof(*man)); + man->x = x; + man->y = y; + + man->rh.x = max_horizontal_extension; + man->rh.y = 0; + man->rh.lr = 1; + man->rh.du = 1; + + man->lh.x = max_horizontal_extension; + man->lh.y = 0; + man->lh.lr = -1; + man->lh.du = 1; + + man->rf.x = max_horizontal_extension; + man->rf.y = max_vertical_extension; + man->rf.lr = 1; + man->rf.du = -1; + + man->lf.x = max_horizontal_extension; + man->lf.y = max_vertical_extension; + man->lf.lr = -1; + man->lf.du = -1; + + copy_all_limbs(man); +} + +static void update_belay(Belay *belay, const Man *man) +{ + belay->b.x = man->x; + belay->b.y = man->y; + belay->l.x = man->x + man->lh.lr * man->lh.x; + belay->l.y = man->y - man->lh.du * man->lh.y; + belay->r.x = man->x + man->rh.lr * man->rh.x; + belay->r.y = man->y - man->rh.du * man->rh.y; +} + +static void init_belay(Belay *belay, const Man *man) +{ + memset(belay, 0, sizeof(*belay)); + belay->l.status = tie_off; + belay->b.status = tie_off; + belay->r.status = tie_off; + update_belay(belay, man); +} + +static void init_pitons(TiePoint pitons[mx_pitons]) +{ + int i; + + for (i = 0; i < mx_pitons; ++i) { + pitons[i].status = tie_off; + pitons[i].x = -1; + pitons[i].y = -1; + pitons[i].tie = NULL; + } +} + +static void init_rope(Rope *rope, int x0, int y0, int xl, int yl) +{ + int i; + + memset(rope, 0, sizeof(*rope)); + rope->x0 = x0; + rope->y0 = y0; + rope->xl = xl; + rope->yl = yl; + rope->end0 = 10; + rope->endl = 10; + + for (i = 0; i < mx_ties; ++i) { + rope->ties[i].status = tie_off; + rope->ties[i].x = NULL; + rope->ties[i].y = NULL; + rope->ties[i].pre = NULL; + rope->ties[i].post = NULL; + } + + rope->ties[0].x = &rope->x0; + rope->ties[0].y = &rope->y0; + rope->ties[1].x = &rope->xl; + rope->ties[1].y = &rope->yl; + rope->ties[0].post = &rope->ties[1]; + rope->ties[1].pre = &rope->ties[0]; +} + +static void dangle_ends(Rope *rope) +{ + Tie *tie = rope->ties[0].post; + + rope->x0 = *tie->x; + rope->y0 = *tie->y + rope->end0; + + tie = rope->ties[1].pre; + rope->xl = *tie->x; + rope->yl = *tie->y + rope->endl; +} + +static int get_free_index(Rope *rope) +{ + int i; + + for (i = 2; i < mx_ties; ++i) { + if (rope->ties[i].pre == NULL && rope->ties[i].post == NULL) { + return i; + } + } + + return mx_ties; +} + +static bool insert_tie(Tie *at, Tie *tie) +{ + if (at->post != NULL) { + tie->pre = at; + tie->post = at->post; + at->post->pre = tie; + at->post = tie; + return true; + } + + if (at->pre != NULL) { + tie->post = at; + tie->pre = at->pre; + at->pre->post = tie; + at->pre = tie; + return true; + } + + return false; +} + +static bool delete_tie(Tie *tie) +{ + if (tie == NULL || tie->pre == NULL || tie->post == NULL) { + return false; + } + + tie->post->pre = tie->pre; + tie->pre->post = tie->post; + tie->status = tie_off; + tie->x = NULL; + tie->y = NULL; + tie->pre = NULL; + tie->post = NULL; + return true; +} + +static bool add_tiepoint(Tie *at, Tie *tie, TiePoint *point, int status) +{ + if (!insert_tie(at, tie)) { + return false; + } + + tie->x = &point->x; + tie->y = &point->y; + tie->status = status; + point->status = status; + point->tie = tie; + return true; +} + +static bool is_near_rope(int a, int b, int x1, int y1, int x2, int y2) +{ + int projected; + const int tolerance = 4; + + if (x1 == x2) { + if (a != x1) { + return false; + } + return (b - y1) * (b - y2) <= 0; + } + + if ((a - x1) * (a - x2) > 0) { + return false; + } + + projected = y1 + (int)(((double)(a - x1) * (double)(y2 - y1)) / (double)(x2 - x1)); + return projected - tolerance <= b && projected + tolerance >= b; +} + +static bool check_rope_touch(int x, int y, Rope *rope, Tie **at) +{ + Tie *tie = &rope->ties[0]; + Tie *next = tie->post; + + while (next != NULL) { + if (is_near_rope(x, y, *tie->x, *tie->y, *next->x, *next->y)) { + *at = tie; + return true; + } + tie = next; + next = next->post; + } + + return false; +} + +static int limb_world_x(const Man *man, const Limb *limb) +{ + return man->x + limb->lr * limb->x; +} + +static int limb_world_y(const Man *man, const Limb *limb) +{ + return man->y - limb->du * limb->y; +} + +static bool is_attached(const Man *man, const Limb *limb, const Ledge ledges[mx_ledges], int ledge_count) +{ + int x = limb_world_x(man, limb); + int y = limb_world_y(man, limb); + int i; + + for (i = 0; i < ledge_count; ++i) { + if (y == ledges[i].height && x >= ledges[i].left_end && x <= ledges[i].right_end) { + return true; + } + } + + return false; +} + +static int ledge_support(const Man *man, const Ledge ledges[mx_ledges], int ledge_count) +{ + int count = 0; + + if (is_attached(man, &man->rh, ledges, ledge_count)) { + count += 1; + } + if (is_attached(man, &man->lh, ledges, ledge_count)) { + count += 1; + } + if (is_attached(man, &man->lf, ledges, ledge_count)) { + count += 2; + } + if (is_attached(man, &man->rf, ledges, ledge_count)) { + count += 2; + } + + return count; +} + +static void swing_body(int *dx, int *dy, const Limb *new_limb, const Limb *old_limb, int direction) +{ + int delta_x = (new_limb->x - old_limb->x) * new_limb->lr; + int delta_y = (new_limb->y - old_limb->y) * new_limb->du; + + if ((delta_x > 0 && direction == dir_left) || (delta_x < 0 && direction == dir_right)) { + *dx = delta_x; + } + if ((delta_y > 0 && direction == dir_down) || (delta_y < 0 && direction == dir_up)) { + *dy = delta_y; + } +} + +static void move_limb(Limb *limb, int sign, int direction) +{ + int value; + + if (direction == dir_left || direction == dir_right) { + value = limb->x + sign * limb_step; + if (value < 0) { + value = 0; + } + if (value > max_horizontal_extension) { + value = max_horizontal_extension; + } + limb->x = value; + } else { + value = limb->y + sign * limb_step; + if (value < 0) { + value = 0; + } + if (value > max_vertical_extension) { + value = max_vertical_extension; + } + limb->y = value; + } +} + +static void move_limb_key(int move, Man *man, int direction) +{ + Limb *limb = NULL; + int sign = 0; + + switch (move) { + case 'p': + limb = &man->rh; + sign = 1; + break; + case 'l': + limb = &man->rh; + sign = -1; + break; + case 'q': + limb = &man->lh; + sign = 1; + break; + case 'a': + limb = &man->lh; + sign = -1; + break; + case 'k': + limb = &man->rf; + sign = (direction == dir_up || direction == dir_down) ? -1 : 1; + break; + case 'm': + limb = &man->rf; + sign = (direction == dir_up || direction == dir_down) ? 1 : -1; + break; + case 's': + limb = &man->lf; + sign = (direction == dir_up || direction == dir_down) ? -1 : 1; + break; + case 'x': + limb = &man->lf; + sign = (direction == dir_up || direction == dir_down) ? 1 : -1; + break; + } + + if (limb != NULL) { + move_limb(limb, sign, direction); + } +} + +static void execute_moves( + int *dx, + int *dy, + Man *man, + const int *moves, + int move_count, + int direction, + const Ledge ledges[mx_ledges], + int ledge_count +) +{ + int i; + int rh_dx = 0; + int rh_dy = 0; + int lh_dx = 0; + int lh_dy = 0; + int rf_dx = 0; + int rf_dy = 0; + int lf_dx = 0; + int lf_dy = 0; + + for (i = 0; i < move_count; ++i) { + move_limb_key(moves[i], man, direction); + } + + if (is_attached(man, &man->old_rh, ledges, ledge_count)) { + swing_body(&rh_dx, &rh_dy, &man->rh, &man->old_rh, direction); + } + if (is_attached(man, &man->old_lh, ledges, ledge_count)) { + swing_body(&lh_dx, &lh_dy, &man->lh, &man->old_lh, direction); + } + if (is_attached(man, &man->old_rf, ledges, ledge_count)) { + swing_body(&rf_dx, &rf_dy, &man->rf, &man->old_rf, direction); + } + if (is_attached(man, &man->old_lf, ledges, ledge_count)) { + swing_body(&lf_dx, &lf_dy, &man->lf, &man->old_lf, direction); + } + + *dx = rh_dx != 0 ? rh_dx : lh_dx != 0 ? lh_dx : rf_dx != 0 ? rf_dx : lf_dx; + *dy = rh_dy != 0 ? rh_dy : lh_dy != 0 ? lh_dy : rf_dy != 0 ? rf_dy : lf_dy; +} + +static int next_tie(Tie **tie, int direction) +{ + if (direction == 1) { + *tie = (*tie)->post; + } else { + *tie = (*tie)->pre; + } + return 0; +} + +static int on_belay(const Belay *belay) +{ + int count = 0; + + if (belay->l.status == tie_on_man) { + count += 1; + } + if (belay->r.status == tie_on_man) { + count += 1; + } + if (belay->b.status == tie_on_man) { + count += 2; + } + + return count; +} + +static int test_pull( + Rope *rope, + Belay *belay, + TiePoint *other_body, + int *pivot_x, + int *pivot_y, + int direction, + int *lift +) +{ + int anchored = 0; + Tie *left_tie = belay->l.tie; + Tie *right_tie = belay->r.tie; + Tie *body_tie = belay->b.tie; + Tie *cursor = direction == 1 ? &rope->ties[0] : &rope->ties[1]; + + while (cursor != NULL) { + if (cursor == left_tie || cursor == right_tie || cursor == body_tie) { + int old_y = *cursor->y; + next_tie(&cursor, -direction); + if (cursor == NULL) { + return 0; + } + *pivot_x = *cursor->x; + *pivot_y = *cursor->y; + *lift = *pivot_y < old_y ? 1 : 0; + break; + } + + if ((cursor == other_body->tie && cursor->status == tie_on_man) || cursor->status == tie_on_ledge) { + anchored = 1; + } + + next_tie(&cursor, direction); + } + + if (cursor == NULL) { + return 0; + } + + return anchored; +} + +static int is_pivot_forward(int pivot_x, int pivot_y, int hand_x, int hand_y, int direction) +{ + switch (direction) { + case dir_up: + return hand_y > pivot_y; + case dir_down: + return hand_y < pivot_y; + case dir_right: + return hand_x < pivot_x; + case dir_left: + return hand_x > pivot_x; + default: + return 0; + } +} + +static void adjust_rope_ends(Rope *rope, int length, int end, int sign) +{ + if (end == 1) { + rope->end0 += sign * length; + if (rope->end0 < 10) { + rope->end0 = 10; + } + } else { + rope->endl += sign * length; + if (rope->endl < 10) { + rope->endl = 10; + } + } +} + +static bool check_limb_centering(const TiePoint *tie, const Limb *hand, int ledgesupport) +{ + if (tie->status != tie_on_man) { + return false; + } + + return (ledgesupport == 0 && hand->x == 0) || ledgesupport > 0; +} + +static void limb_rope_move( + Game *game, + const Limb *limb, + const Limb *old_limb, + Belay *belay, + TiePoint *tie, + TiePoint *other_body, + int *dx, + int *dy, + int direction +) +{ + int temp_dx = 0; + int temp_dy = 0; + int pivot_x = 0; + int pivot_y = 0; + int lift = 0; + int in_pivot = 0; + int out_pivot = 0; + int in_pull = 0; + int out_pull = 0; + + if (tie->status != tie_on_man) { + return; + } + + swing_body(&temp_dx, &temp_dy, limb, old_limb, direction); + if (temp_dx == 0 && temp_dy == 0) { + return; + } + + in_pull = test_pull(&game->rope, belay, other_body, &pivot_x, &pivot_y, 1, &lift); + in_pivot = is_pivot_forward(pivot_x, pivot_y, tie->x, tie->y, direction); + out_pull = test_pull(&game->rope, belay, other_body, &pivot_x, &pivot_y, -1, &lift); + out_pivot = is_pivot_forward(pivot_x, pivot_y, tie->x, tie->y, direction); + + if (in_pivot) { + if (in_pull) { + *dx = temp_dx; + *dy = temp_dy; + } else { + adjust_rope_ends(&game->rope, abs(temp_dx - temp_dy), 1, -1); + } + + if (out_pull == 0 && belay->b.status != tie_on_man) { + adjust_rope_ends(&game->rope, abs(temp_dx - temp_dy), -1, 1); + } + } + + if (out_pivot) { + if (out_pull) { + *dx = temp_dx; + *dy = temp_dy; + } else { + adjust_rope_ends(&game->rope, abs(temp_dx - temp_dy), -1, -1); + } + + if (in_pull == 0 && belay->b.status != tie_on_man) { + adjust_rope_ends(&game->rope, abs(temp_dx - temp_dy), 1, 1); + } + } +} + +static void execute_rope_moves(Game *game, Man *man, int *dx, int *dy, int ledgesupport) +{ + TiePoint *other_body = &game->belays[(game->active_man + 1) % 2].b; + Belay *belay = &game->belays[game->active_man]; + + if (check_limb_centering(&belay->l, &man->lh, ledgesupport)) { + limb_rope_move( + game, + &man->lh, + &man->old_lh, + belay, + &belay->l, + other_body, + dx, + dy, + game->direction + ); + } + + if (check_limb_centering(&belay->r, &man->rh, ledgesupport)) { + limb_rope_move( + game, + &man->rh, + &man->old_rh, + belay, + &belay->r, + other_body, + dx, + dy, + game->direction + ); + } +} + +static int circle_y(int x, int center_x, int center_y, double radius) +{ + double argument = radius * radius - (double)((x - center_x) * (x - center_x)); + + if (argument > 0.0) { + return center_y + (int)sqrt(argument); + } + return center_y; +} + +static void replace_man(Game *game, Man *man, Belay *belay, int x, int y) +{ + man->x = x; + man->y = y; + update_belay(belay, man); + dangle_ends(&game->rope); + animate(game, 16); +} + +static int start_fall(Game *game, Man *man, Belay *belay) +{ + int start_y = man->y; + + while ((man->y - start_y) < 100) { + if (ledge_support(man, game->level.ledges, game->level.count) > 2) { + return 0; + } + + man->y += 10; + update_belay(belay, man); + dangle_ends(&game->rope); + animate(game, 24); + } + + return -1; +} + +static int hang_drop(Game *game, Man *man, Belay *belay, TiePoint *other_body) +{ + int in_lift = 0; + int out_lift = 0; + int in_x = 0; + int in_y = 0; + int out_x = 0; + int out_y = 0; + int start_y = man->y; + + while ((in_lift == 0 || out_lift == 0) && (man->y - start_y) < 100) { + man->y += 1; + update_belay(belay, man); + dangle_ends(&game->rope); + animate(game, 8); + + if (in_lift + out_lift + ledge_support(man, game->level.ledges, game->level.count) > 2) { + return 0; + } + + test_pull(&game->rope, belay, other_body, &in_x, &in_y, 1, &in_lift); + test_pull(&game->rope, belay, other_body, &out_x, &out_y, -1, &out_lift); + } + + if ((man->y - start_y) == 100) { + return -1; + } + + man->x = 10 * ((man->x + 5) / 10); + man->y = 10 * ((man->y + 5) / 10); + return 0; +} + +static int swing_baby(Game *game, Man *man, Belay *belay, int pivot_x, int pivot_y, int in_lift, int out_lift) +{ + int x = man->x; + int y = man->y; + int sign = 0; + int limit; + double u; + double v; + double radius; + + if (x < pivot_x) { + sign = 1; + } else if (x > pivot_x) { + sign = -1; + } + + limit = pivot_y + 20; + while (y < limit) { + y += 5; + replace_man(game, man, belay, x, y); + if (in_lift + out_lift + ledge_support(man, game->level.ledges, game->level.count) > 2) { + return 0; + } + } + + u = (double)(x - pivot_x); + v = (double)(y - pivot_y); + radius = sqrt(u * u + v * v); + + if (sign == 0) { + return 0; + } + + limit = pivot_x + (pivot_x - x); + if (sign > 0) { + limit -= 20; + } else { + limit += 20; + } + + while (sign * x < sign * limit) { + x += sign * 5; + y = circle_y(x, pivot_x, pivot_y, radius); + replace_man(game, man, belay, x, y); + if (in_lift + out_lift + ledge_support(man, game->level.ledges, game->level.count) > 2) { + return 0; + } + } + + replace_man(game, man, belay, 10 * ((man->x + 5) / 10), 10 * ((man->y + 5) / 10)); + return 0; +} + +static int new_settle_weight(Game *game, Man *man) +{ + int ledgesupport = ledge_support(man, game->level.ledges, game->level.count); + int in_pull; + int out_pull; + int in_lift = 0; + int out_lift = 0; + int in_x = 0; + int in_y = 0; + int out_x = 0; + int out_y = 0; + TiePoint *other_body; + Belay *belay = &game->belays[game->active_man]; + + if (ledgesupport > 2) { + return 0; + } + if (on_belay(belay) < 2) { + return start_fall(game, man, belay); + } + + other_body = &game->belays[(game->active_man + 1) % 2].b; + in_pull = test_pull(&game->rope, belay, other_body, &in_x, &in_y, 1, &in_lift); + out_pull = test_pull(&game->rope, belay, other_body, &out_x, &out_y, -1, &out_lift); + + if (in_lift + out_lift + ledgesupport > 2) { + return 0; + } + if (in_pull == 1 && out_pull == 0) { + return swing_baby(game, man, belay, in_x, in_y, in_lift, out_lift); + } + if (in_pull == 0 && out_pull == 1) { + return swing_baby(game, man, belay, out_x, out_y, in_lift, out_lift); + } + if (in_pull == 0 && out_pull == 0) { + return start_fall(game, man, belay); + } + if ((in_x <= man->x && man->x <= out_x) || (out_x <= man->x && man->x <= in_x)) { + return hang_drop(game, man, belay, other_body); + } + if (abs(in_x - man->x) < abs(out_x - man->x)) { + return swing_baby(game, man, belay, in_x, in_y, in_lift, out_lift); + } + return swing_baby(game, man, belay, out_x, out_y, in_lift, out_lift); +} + +static int move_man(Game *game) +{ + int dx = 0; + int dy = 0; + int support = 0; + int result = 0; + Man *man = current_man(game); + + execute_moves( + &dx, + &dy, + man, + game->move_queue, + game->queued_moves, + game->direction, + game->level.ledges, + game->level.count + ); + + if (dx == 0 && dy == 0) { + support = ledge_support(man, game->level.ledges, game->level.count); + execute_rope_moves(game, man, &dx, &dy, support); + } + + man->x -= dx; + man->y += dy; + copy_all_limbs(man); + update_belay(&game->belays[game->active_man], man); + dangle_ends(&game->rope); + + result = new_settle_weight(game, man); + animate(game, 8); + return result; +} + +static void clear_move_batch(Game *game) +{ + game->queued_moves = 0; + game->move_batch_active = false; +} + +static bool man_on_top(const Game *game) +{ + const Man *man = &game->men[game->active_man]; + const Ledge *top = &game->level.ledges[0]; + + return man->y + man->lf.y == top->height || man->y + man->rf.y == top->height; +} + +static void belay_to_rope(Game *game, TiePoint *point, int state_count) +{ + int index; + Tie *at = NULL; + + if (point->status == tie_on_man && state_count == 3) { + point->status = tie_on_man_sliding; + point->tie->status = tie_on_man_sliding; + return; + } + + if (point->status == tie_on_man || point->status == tie_on_man_sliding) { + if (delete_tie(point->tie)) { + point->status = tie_off; + point->tie = NULL; + } + return; + } + + if (!check_rope_touch(point->x, point->y, &game->rope, &at)) { + return; + } + + index = get_free_index(&game->rope); + if (index == mx_ties) { + return; + } + + add_tiepoint(at, &game->rope.ties[index], point, tie_on_man); +} + +static void process_belay_action(Game *game, int key) +{ + Belay *belay = &game->belays[game->active_man]; + TiePoint *point = NULL; + int state_count = 0; + + switch (key) { + case 'w': + point = &belay->l; + state_count = 2; + break; + case 'b': + point = &belay->b; + state_count = 3; + break; + case 'o': + point = &belay->r; + state_count = 2; + break; + default: + return; + } + + belay_to_rope(game, point, state_count); + dangle_ends(&game->rope); +} + +static int get_piton_index(TiePoint pitons[mx_pitons], int x, int y) +{ + int i; + + for (i = 0; i < mx_pitons; ++i) { + if (pitons[i].x == x && pitons[i].y == y) { + return i; + } + } + + for (i = 0; i < mx_pitons; ++i) { + if (pitons[i].status == tie_off) { + return i; + } + } + + return mx_pitons; +} + +static void piton_to_rope(Game *game, TiePoint *piton, Tie *at, int x, int y) +{ + int index; + + switch (piton->status) { + case tie_on_ledge: + piton->status = tie_on_ledge_sliding; + piton->tie->status = tie_on_ledge_sliding; + return; + case tie_on_ledge_sliding: + delete_tie(piton->tie); + piton->status = tie_off; + piton->tie = NULL; + return; + default: + index = get_free_index(&game->rope); + if (index == mx_ties) { + return; + } + piton->status = tie_on_ledge; + piton->x = x; + piton->y = y; + add_tiepoint(at, &game->rope.ties[index], piton, tie_on_ledge); + return; + } +} + +static void process_piton_action(Game *game, int key) +{ + Man *man = current_man(game); + Tie *at = NULL; + int x = 0; + int y = 0; + int index; + + if (key == 'i') { + if (!is_attached(man, &man->rh, game->level.ledges, game->level.count)) { + return; + } + x = limb_world_x(man, &man->rh); + y = limb_world_y(man, &man->rh); + } else if (key == 'e') { + if (!is_attached(man, &man->lh, game->level.ledges, game->level.count)) { + return; + } + x = limb_world_x(man, &man->lh); + y = limb_world_y(man, &man->lh); + } else { + return; + } + + if (!check_rope_touch(x, y, &game->rope, &at)) { + return; + } + + index = get_piton_index(game->pitons, x, y); + if (index == mx_pitons) { + return; + } + + piton_to_rope(game, &game->pitons[index], at, x, y); + dangle_ends(&game->rope); +} + +static void process_pending_actions(Game *game) +{ + if (game->pending_man >= 0) { + game->active_man = game->pending_man; + game->pending_man = -1; + } + + if (game->pending_belay_key != 0) { + process_belay_action(game, game->pending_belay_key); + game->pending_belay_key = 0; + } + + if (game->pending_piton_key != 0) { + process_piton_action(game, game->pending_piton_key); + game->pending_piton_key = 0; + } +} + +static bool is_move_key(int key) +{ + switch (key) { + case 'p': + case 'l': + case 'k': + case 'm': + case 'q': + case 'a': + case 's': + case 'x': + return true; + default: + return false; + } +} + +bool game_init( + Game *game, + const char *level_name, + const RenderHooks *hooks, + uint64_t now_ms, + char *error, + size_t error_size +) +{ + memset(game, 0, sizeof(*game)); + + if (!load_level(&game->level, level_name, error, error_size)) { + return false; + } + + if (hooks != NULL) { + game->hooks = *hooks; + } + + init_man(&game->men[active_man_dougal], 110, 250); + init_belay(&game->belays[active_man_dougal], &game->men[active_man_dougal]); + + init_man(&game->men[active_man_peter], 70, 250); + init_belay(&game->belays[active_man_peter], &game->men[active_man_peter]); + + init_rope(&game->rope, 70, 250, 110, 250); + init_pitons(game->pitons); + + game->active_man = active_man_dougal; + game->direction = dir_right; + game->pending_man = -1; + game->started_ms = now_ms; + + belay_to_rope(game, &game->belays[active_man_dougal].b, 3); + belay_to_rope(game, &game->belays[active_man_peter].b, 3); + dangle_ends(&game->rope); + + return true; +} + +void game_press_key(Game *game, int key, uint64_t now_ms) +{ + if (game->round_over) { + return; + } + + if (key >= 0 && key < 256) { + key = tolower(key); + } + + switch (key) { + case key_escape: + game->result = result_retreat; + game->round_over = true; + clear_move_batch(game); + return; + case key_f1: + game->pending_man = active_man_peter; + return; + case key_f2: + game->pending_man = active_man_dougal; + return; + case key_arrow_left: + game->direction = dir_left; + return; + case key_arrow_right: + game->direction = dir_right; + return; + case key_arrow_up: + game->direction = dir_up; + return; + case key_arrow_down: + game->direction = dir_down; + return; + case 'w': + case 'b': + case 'o': + game->pending_belay_key = key; + return; + case 'e': + case 'i': + game->pending_piton_key = key; + return; + default: + break; + } + + if (!is_move_key(key)) { + return; + } + + if (game->queued_moves < mx_moves) { + game->move_queue[game->queued_moves++] = key; + } + if (!game->move_batch_active) { + game->move_batch_active = true; + game->move_batch_started_ms = now_ms; + } +} + +void game_update(Game *game, uint64_t now_ms) +{ + if (game->round_over) { + return; + } + + if (game->move_batch_active && now_ms - game->move_batch_started_ms >= 150) { + int move_result; + + game->move_count++; + move_result = move_man(game); + clear_move_batch(game); + + if (move_result == -1) { + game->result = result_fall; + game->round_over = true; + return; + } + + if (move_result == 0 && man_on_top(game)) { + game->result = result_win; + game->round_over = true; + return; + } + } + + if (!game->move_batch_active) { + process_pending_actions(game); + } +} + +int game_elapsed_seconds(const Game *game, uint64_t now_ms) +{ + return (int)((now_ms - game->started_ms) / 1000); +} + +const char *game_result_title(RoundResult result) +{ + switch (result) { + case result_fall: + return "A LONG WAY DOWN"; + case result_win: + return "TO THE TOP"; + case result_retreat: + return "RETREAT"; + default: + return ""; + } +} + +const char *game_result_line(RoundResult result, int line) +{ + static const char *fall_lines[] = { + "YOUR CLIMBING COMPANION WATCHES IN SHOCKED HORROR.", + "YOU SLIDE DOWN A SHORT ICEFIELD, THEN CARTWHEEL", + "OUT OVER THE 3,000 FOOT ABYSS.", + "PRESS ENTER TO CLIMB AGAIN OR ESC TO QUIT." + }; + static const char *win_lines[] = { + "IT IS AMAZING. YOU MADE IT TO THE TOP.", + "THE VIEW OF THE SURROUNDING PEAKS IS SPECTACULAR.", + "YOU BARELY NOTICE THE FROSTBITE SETTING IN.", + "PRESS ENTER TO CLIMB AGAIN OR ESC TO QUIT." + }; + static const char *retreat_lines[] = { + "YOU DECIDE TO RETREAT FOR TODAY,", + "BUT YOU ARE HAPPY KNOWING YOU'LL BE BACK.", + "", + "PRESS ENTER TO CLIMB AGAIN OR ESC TO QUIT." + }; + const char **lines = NULL; + + switch (result) { + case result_fall: + lines = fall_lines; + break; + case result_win: + lines = win_lines; + break; + case result_retreat: + lines = retreat_lines; + break; + default: + return ""; + } + + if (line < 0 || line > 3) { + return ""; + } + + return lines[line]; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..cd5589b --- /dev/null +++ b/src/main.c @@ -0,0 +1,165 @@ +#include +#include + +#include + +#include "cliffs.h" +#include "sdl_app.h" + +typedef enum { + screen_instructions = 0, + screen_playing, + screen_result +} Screen; + +static void usage(const char *argv0) +{ + fprintf(stderr, "usage: %s [level-name-or-path] [--smoke-test]\n", argv0); +} + +static int map_key(const SDL_KeyboardEvent *key) +{ + switch (key->keysym.sym) { + case SDLK_ESCAPE: + return key_escape; + case SDLK_RETURN: + case SDLK_KP_ENTER: + return key_enter; + case SDLK_UP: + return key_arrow_up; + case SDLK_DOWN: + return key_arrow_down; + case SDLK_LEFT: + return key_arrow_left; + case SDLK_RIGHT: + return key_arrow_right; + case SDLK_F1: + return key_f1; + case SDLK_F2: + return key_f2; + default: + if (key->keysym.sym >= 0 && key->keysym.sym < 128) { + return (int)key->keysym.sym; + } + return key_none; + } +} + +int main(int argc, char **argv) +{ + App app; + Game game; + RenderHooks hooks; + Screen screen = screen_instructions; + uint64_t now_ms; + bool running = true; + bool smoke_test = false; + bool tune_started = false; + const char *level_name = "ledges"; + char error[256]; + int i; + + for (i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--smoke-test") == 0) { + smoke_test = true; + } else if (argv[i][0] == '-' && argv[i][1] != '\0') { + usage(argv[0]); + return 1; + } else { + level_name = argv[i]; + } + } + + if (!app_init(&app, smoke_test, error, sizeof(error))) { + fprintf(stderr, "%s\n", error); + return 1; + } + + app_load_win_image(&app, "assets/ev6.pik", error, sizeof(error)); + + hooks.present = app_render_hook; + hooks.delay = app_delay_hook; + hooks.userdata = &app; + + now_ms = SDL_GetTicks64(); + if (!game_init(&game, level_name, &hooks, now_ms, error, sizeof(error))) { + fprintf(stderr, "%s\n", error); + app_shutdown(&app); + return 1; + } + + if (smoke_test) { + app_render_game(&app, &game, now_ms); + app_present(&app); + app_shutdown(&app); + return 0; + } + + while (running) { + SDL_Event event; + + now_ms = SDL_GetTicks64(); + + if (screen == screen_instructions && !tune_started) { + app_play_intro_tune(&app); + tune_started = true; + } + + while (SDL_PollEvent(&event) != 0) { + if (event.type == SDL_QUIT) { + running = false; + break; + } + + if (event.type == SDL_KEYDOWN && event.key.repeat == 0) { + int key = map_key(&event.key); + + if (screen == screen_instructions) { + if (key == key_escape) { + running = false; + } else if (key == key_enter) { + app_stop_audio(&app); + tune_started = false; + now_ms = SDL_GetTicks64(); + if (!game_init(&game, level_name, &hooks, now_ms, error, sizeof(error))) { + fprintf(stderr, "%s\n", error); + running = false; + } else { + screen = screen_playing; + } + } + } else if (screen == screen_playing) { + game_press_key(&game, key, now_ms); + } else if (screen == screen_result) { + if (key == key_escape) { + running = false; + } else if (key == key_enter) { + screen = screen_instructions; + } + } + } + } + + if (!running) { + break; + } + + if (screen == screen_playing) { + game_update(&game, now_ms); + if (game.round_over) { + screen = screen_result; + } + app_render_game(&app, &game, now_ms); + } else if (screen == screen_instructions) { + app_render_instructions(&app, level_name); + } else { + app_render_result(&app, &game); + } + + app_present(&app); + SDL_Delay(16); + } + + app_shutdown(&app); + return 0; +} diff --git a/src/sdl_app.c b/src/sdl_app.c new file mode 100644 index 0000000..26cd493 --- /dev/null +++ b/src/sdl_app.c @@ -0,0 +1,659 @@ +#include "sdl_app.h" + +#include +#include +#include +#include +#include + +enum { + color_black = 0, + color_blue = 1, + color_green = 2, + color_red = 4, + color_grey = 8, + color_light_blue = 11, + color_light_red = 12, + color_yellow = 14, + color_white = 15 +}; + +typedef struct { + char ch; + uint8_t rows[7]; +} Glyph; + +typedef struct { + unsigned pitch; + unsigned duration; +} Tone; + +static const SDL_Color ega_palette[16] = { + {0, 0, 0, 255}, + {0, 0, 170, 255}, + {0, 170, 0, 255}, + {0, 170, 170, 255}, + {170, 0, 0, 255}, + {170, 0, 170, 255}, + {170, 85, 0, 255}, + {170, 170, 170, 255}, + {85, 85, 85, 255}, + {85, 85, 255, 255}, + {85, 255, 85, 255}, + {85, 255, 255, 255}, + {255, 85, 85, 255}, + {255, 85, 255, 255}, + {255, 255, 85, 255}, + {255, 255, 255, 255} +}; + +static const Glyph glyphs[] = { + {'A', {0x0e, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11}}, + {'B', {0x1e, 0x11, 0x11, 0x1e, 0x11, 0x11, 0x1e}}, + {'C', {0x0e, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0e}}, + {'D', {0x1c, 0x12, 0x11, 0x11, 0x11, 0x12, 0x1c}}, + {'E', {0x1f, 0x10, 0x10, 0x1e, 0x10, 0x10, 0x1f}}, + {'F', {0x1f, 0x10, 0x10, 0x1e, 0x10, 0x10, 0x10}}, + {'G', {0x0e, 0x11, 0x10, 0x17, 0x11, 0x11, 0x0f}}, + {'H', {0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11}}, + {'I', {0x0e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e}}, + {'J', {0x01, 0x01, 0x01, 0x01, 0x11, 0x11, 0x0e}}, + {'K', {0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11}}, + {'L', {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1f}}, + {'M', {0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11}}, + {'N', {0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x11}}, + {'O', {0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e}}, + {'P', {0x1e, 0x11, 0x11, 0x1e, 0x10, 0x10, 0x10}}, + {'Q', {0x0e, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0d}}, + {'R', {0x1e, 0x11, 0x11, 0x1e, 0x14, 0x12, 0x11}}, + {'S', {0x0f, 0x10, 0x10, 0x0e, 0x01, 0x01, 0x1e}}, + {'T', {0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}}, + {'U', {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e}}, + {'V', {0x11, 0x11, 0x11, 0x11, 0x11, 0x0a, 0x04}}, + {'W', {0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0a}}, + {'X', {0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11}}, + {'Y', {0x11, 0x11, 0x0a, 0x04, 0x04, 0x04, 0x04}}, + {'Z', {0x1f, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1f}}, + {'0', {0x0e, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0e}}, + {'1', {0x04, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x0e}}, + {'2', {0x0e, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1f}}, + {'3', {0x1f, 0x02, 0x04, 0x02, 0x01, 0x11, 0x0e}}, + {'4', {0x02, 0x06, 0x0a, 0x12, 0x1f, 0x02, 0x02}}, + {'5', {0x1f, 0x10, 0x1e, 0x01, 0x01, 0x11, 0x0e}}, + {'6', {0x06, 0x08, 0x10, 0x1e, 0x11, 0x11, 0x0e}}, + {'7', {0x1f, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08}}, + {'8', {0x0e, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x0e}}, + {'9', {0x0e, 0x11, 0x11, 0x0f, 0x01, 0x02, 0x0c}}, + {':', {0x00, 0x04, 0x04, 0x00, 0x04, 0x04, 0x00}}, + {'.', {0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c}}, + {',', {0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x08}}, + {'!', {0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x04}}, + {'?', {0x0e, 0x11, 0x01, 0x02, 0x04, 0x00, 0x04}}, + {'-', {0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00}}, + {'=', {0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00}}, + {'+', {0x00, 0x04, 0x04, 0x1f, 0x04, 0x04, 0x00}}, + {'/', {0x01, 0x02, 0x02, 0x04, 0x08, 0x08, 0x10}}, + {'<', {0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02}}, + {'>', {0x08, 0x04, 0x02, 0x01, 0x02, 0x04, 0x08}}, + {'(', {0x02, 0x04, 0x08, 0x08, 0x08, 0x04, 0x02}}, + {')', {0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08}}, + {'\'', {0x04, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00}}, + {'"', {0x0a, 0x0a, 0x04, 0x00, 0x00, 0x00, 0x00}}, + {' ', {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} +}; + +static const Tone intro_tune[] = { + {8000, 5}, {6000, 10}, {5400, 10}, {4750, 5}, {4000, 5}, {3500, 5}, {3000, 5}, + {4000, 10}, {4750, 5}, {4500, 5}, {4000, 10}, {4750, 5}, {4000, 5}, + {3500, 10}, {4750, 10}, {4000, 30}, + {100, 25}, {4750, 5}, + {3500, 10}, {3750, 10}, {4750, 5}, {5650, 5}, {5400, 5}, {4250, 5}, + {3500, 10}, {2825, 10}, {4750, 10}, {100, 5}, {3500, 5}, + {3150, 20}, {3500, 10}, {100, 10}, + {3150, 25}, {3500, 5}, {4500, 5}, {6400, 5}, {6000, 10}, + {24000, 5}, {8000, 5}, {6000, 5}, {4750, 10}, {3000, 40}, + {0, 0} +}; + +static const uint8_t *glyph_rows(char c) +{ + size_t i; + unsigned char upper = (unsigned char)toupper((unsigned char)c); + + for (i = 0; i < sizeof(glyphs) / sizeof(glyphs[0]); ++i) { + if ((unsigned char)glyphs[i].ch == upper) { + return glyphs[i].rows; + } + } + + return glyphs[sizeof(glyphs) / sizeof(glyphs[0]) - 1].rows; +} + +static void set_color(App *app, int index) +{ + SDL_Color color = ega_palette[index & 15]; + + SDL_SetRenderDrawColor(app->renderer, color.r, color.g, color.b, 255); +} + +static void clear_screen(App *app, int color_index) +{ + set_color(app, color_index); + SDL_RenderClear(app->renderer); +} + +static void draw_ellipse(App *app, int x1, int y1, int x2, int y2, bool filled) +{ + int center_x = (x1 + x2) / 2; + int center_y = (y1 + y2) / 2; + int rx = abs(x2 - x1) / 2; + int ry = abs(y2 - y1) / 2; + int y; + + if (rx <= 0 && ry <= 0) { + SDL_RenderDrawPoint(app->renderer, center_x, center_y); + return; + } + + if (rx <= 0) { + SDL_RenderDrawLine(app->renderer, center_x, y1, center_x, y2); + return; + } + if (ry <= 0) { + SDL_RenderDrawLine(app->renderer, x1, center_y, x2, center_y); + return; + } + + for (y = -ry; y <= ry; ++y) { + double ratio = 1.0 - ((double)(y * y) / (double)(ry * ry)); + int span = (int)lround((double)rx * sqrt(ratio < 0.0 ? 0.0 : ratio)); + + if (filled) { + SDL_RenderDrawLine(app->renderer, center_x - span, center_y + y, center_x + span, center_y + y); + } else { + SDL_RenderDrawPoint(app->renderer, center_x - span, center_y + y); + SDL_RenderDrawPoint(app->renderer, center_x + span, center_y + y); + } + } +} + +static void draw_char(App *app, int x, int y, char c, int color_index) +{ + const uint8_t *rows = glyph_rows(c); + int row; + int col; + + set_color(app, color_index); + for (row = 0; row < 7; ++row) { + for (col = 0; col < 5; ++col) { + if (rows[row] & (1 << (4 - col))) { + SDL_RenderDrawPoint(app->renderer, x + col, y + row); + } + } + } +} + +static void draw_text(App *app, int col, int row, int color_index, const char *text) +{ + int x = (col - 1) * 8; + int y = (row - 1) * 8; + int start_x = x; + const unsigned char *cursor = (const unsigned char *)text; + + while (*cursor != '\0') { + if (*cursor == '\n') { + y += 8; + x = start_x; + } else { + draw_char(app, x, y, (char)*cursor, color_index); + x += 8; + } + cursor++; + } +} + +static void draw_ledge(App *app, const Ledge *ledge) +{ + set_color(app, color_grey); + SDL_RenderDrawLine(app->renderer, ledge->left_end, ledge->height, ledge->right_end, ledge->height); + SDL_RenderDrawLine(app->renderer, ledge->left_end, ledge->height - 1, ledge->right_end, ledge->height - 1); +} + +static void draw_man(App *app, const Man *man, int color_index) +{ + int body_top_x = man->x; + int body_top_y = man->y - 5; + int body_bottom_y = man->y; + + set_color(app, color_index); + SDL_RenderDrawLine(app->renderer, body_top_x, body_top_y, body_top_x, body_bottom_y); + SDL_RenderDrawLine(app->renderer, body_top_x, body_top_y, body_top_x + man->lh.lr * man->lh.x, body_bottom_y - man->lh.du * man->lh.y); + SDL_RenderDrawLine(app->renderer, body_top_x, body_top_y, body_top_x + man->rh.lr * man->rh.x, body_bottom_y - man->rh.du * man->rh.y); + SDL_RenderDrawLine(app->renderer, body_top_x, body_bottom_y, body_top_x + man->lf.lr * man->lf.x, body_bottom_y - man->lf.du * man->lf.y); + SDL_RenderDrawLine(app->renderer, body_top_x, body_bottom_y, body_top_x + man->rf.lr * man->rf.x, body_bottom_y - man->rf.du * man->rf.y); + draw_ellipse(app, body_top_x - 2, body_top_y - 8, body_top_x + 2, body_top_y - 3, false); +} + +static void draw_rope(App *app, const Rope *rope) +{ + const Tie *tie = &rope->ties[0]; + const Tie *next = tie->post; + + set_color(app, color_light_red); + while (next != NULL) { + SDL_RenderDrawLine(app->renderer, *tie->x, *tie->y, *next->x, *next->y); + tie = next; + next = next->post; + } +} + +static void draw_arrow(App *app, int direction) +{ + int x = 535; + int y = 445; + int tip_x; + int tip_y; + + set_color(app, color_yellow); + switch (direction) { + case dir_up: + tip_x = x + 10; + tip_y = y + 3; + SDL_RenderDrawLine(app->renderer, tip_x, tip_y, tip_x, y + 20); + SDL_RenderDrawLine(app->renderer, tip_x + 1, tip_y, tip_x + 1, y + 20); + SDL_RenderDrawLine(app->renderer, tip_x, tip_y, x + 3, y + 10); + SDL_RenderDrawLine(app->renderer, tip_x, tip_y, x + 17, y + 10); + break; + case dir_down: + tip_x = x + 10; + tip_y = y + 17; + SDL_RenderDrawLine(app->renderer, tip_x, tip_y, tip_x, y); + SDL_RenderDrawLine(app->renderer, tip_x + 1, tip_y, tip_x + 1, y); + SDL_RenderDrawLine(app->renderer, tip_x, tip_y, x + 3, y + 10); + SDL_RenderDrawLine(app->renderer, tip_x, tip_y, x + 17, y + 10); + break; + case dir_left: + tip_x = x + 3; + tip_y = y + 10; + SDL_RenderDrawLine(app->renderer, tip_x, tip_y, x + 20, tip_y); + SDL_RenderDrawLine(app->renderer, tip_x, tip_y + 1, x + 20, tip_y + 1); + SDL_RenderDrawLine(app->renderer, tip_x, tip_y, x + 10, y + 3); + SDL_RenderDrawLine(app->renderer, tip_x, tip_y, x + 10, y + 17); + break; + case dir_right: + default: + tip_x = x + 17; + tip_y = y + 10; + SDL_RenderDrawLine(app->renderer, tip_x, tip_y, x, tip_y); + SDL_RenderDrawLine(app->renderer, tip_x, tip_y + 1, x, tip_y + 1); + SDL_RenderDrawLine(app->renderer, tip_x, tip_y, x + 10, y + 3); + SDL_RenderDrawLine(app->renderer, tip_x, tip_y, x + 10, y + 17); + break; + } +} + +static void draw_dot(App *app, int x, int y, int fill_color, bool fill) +{ + set_color(app, fill_color); + draw_ellipse(app, x - 2, y - 2, x + 2, y + 2, fill); +} + +static void draw_belay_status(App *app, const Belay *belay, int origin_x, int origin_y) +{ + int x = origin_x; + + if (belay->l.status == tie_off) { + draw_dot(app, x, origin_y, color_yellow, false); + } else { + draw_dot(app, x, origin_y, color_yellow, true); + } + + x += 7; + if (belay->b.status == tie_off) { + draw_dot(app, x, origin_y, color_yellow, false); + } else if (belay->b.status == tie_on_man_sliding) { + draw_dot(app, x, origin_y, color_light_blue, true); + } else { + draw_dot(app, x, origin_y, color_yellow, true); + } + + x += 7; + if (belay->r.status == tie_off) { + draw_dot(app, x, origin_y, color_yellow, false); + } else { + draw_dot(app, x, origin_y, color_yellow, true); + } +} + +static void draw_pitons(App *app, const TiePoint pitons[mx_pitons]) +{ + int i; + + for (i = 0; i < mx_pitons; ++i) { + if (pitons[i].status == tie_on_ledge) { + draw_dot(app, pitons[i].x, pitons[i].y, color_yellow, true); + } else if (pitons[i].status == tie_on_ledge_sliding) { + draw_dot(app, pitons[i].x, pitons[i].y, color_light_blue, true); + } + } +} + +static void draw_round_hud(App *app, const Game *game, uint64_t now_ms) +{ + char buffer[64]; + + draw_text(app, 15, 29, game->active_man == 0 ? color_yellow : color_light_blue, "DOUGALH:"); + draw_text(app, 1, 29, game->active_man == 1 ? color_yellow : color_light_blue, "PETERB:"); + + snprintf(buffer, sizeof(buffer), "MOVE = %d", game->move_count); + draw_text(app, 40, 29, color_yellow, buffer); + + snprintf(buffer, sizeof(buffer), "%d:%02d", game_elapsed_seconds(game, now_ms) / 60, game_elapsed_seconds(game, now_ms) % 60); + draw_text(app, 56, 29, color_yellow, buffer); + + draw_belay_status(app, &game->belays[0], 191, 457); + draw_belay_status(app, &game->belays[1], 71, 457); + draw_arrow(app, game->direction); +} + +static void draw_level_name(App *app, const char *path) +{ + const char *base = strrchr(path, '/'); + char buffer[64]; + + base = base == NULL ? path : base + 1; + snprintf(buffer, sizeof(buffer), "LEVEL: %s", base); + draw_text(app, 1, 1, color_white, buffer); +} + +bool app_init(App *app, bool hidden, char *error, size_t error_size) +{ + Uint32 window_flags = SDL_WINDOW_RESIZABLE; + + memset(app, 0, sizeof(*app)); + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS) != 0) { + snprintf(error, error_size, "sdl init failed: %s", SDL_GetError()); + return false; + } + + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); + + if (hidden) { + window_flags |= SDL_WINDOW_HIDDEN; + } + + app->window = SDL_CreateWindow( + "Attack the Cliffs", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + screen_width * 2, + screen_height * 2, + window_flags + ); + if (app->window == NULL) { + snprintf(error, error_size, "window creation failed: %s", SDL_GetError()); + app_shutdown(app); + return false; + } + + app->renderer = SDL_CreateRenderer(app->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + if (app->renderer == NULL) { + app->renderer = SDL_CreateRenderer(app->window, -1, SDL_RENDERER_SOFTWARE); + } + if (app->renderer == NULL) { + snprintf(error, error_size, "renderer creation failed: %s", SDL_GetError()); + app_shutdown(app); + return false; + } + + SDL_RenderSetLogicalSize(app->renderer, screen_width, screen_height); + SDL_RenderSetIntegerScale(app->renderer, SDL_TRUE); + + { + SDL_AudioSpec want; + SDL_AudioSpec have; + + SDL_zero(want); + want.freq = 48000; + want.format = AUDIO_F32SYS; + want.channels = 1; + want.samples = 1024; + + app->audio_device = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0); + if (app->audio_device != 0) { + app->audio_ok = true; + SDL_PauseAudioDevice(app->audio_device, 0); + } + } + + return true; +} + +void app_shutdown(App *app) +{ + if (app->win_texture != NULL) { + SDL_DestroyTexture(app->win_texture); + app->win_texture = NULL; + } + if (app->audio_device != 0) { + SDL_CloseAudioDevice(app->audio_device); + app->audio_device = 0; + } + if (app->renderer != NULL) { + SDL_DestroyRenderer(app->renderer); + app->renderer = NULL; + } + if (app->window != NULL) { + SDL_DestroyWindow(app->window); + app->window = NULL; + } + SDL_Quit(); +} + +static void queue_tone(App *app, unsigned pitch, unsigned duration_units) +{ + float *samples; + int sample_count; + int i; + const int sample_rate = 48000; + const float seconds = (float)duration_units * 0.05f; + + if (!app->audio_ok || duration_units == 0) { + return; + } + + sample_count = (int)(seconds * (float)sample_rate); + if (sample_count <= 0) { + return; + } + + samples = (float *)malloc((size_t)sample_count * sizeof(float)); + if (samples == NULL) { + return; + } + + if (pitch == 100) { + memset(samples, 0, (size_t)sample_count * sizeof(float)); + } else { + float frequency = 1193180.0f / (float)pitch; + float phase = 0.0f; + float phase_step = frequency / (float)sample_rate; + + for (i = 0; i < sample_count; ++i) { + float envelope = 1.0f; + + if (i > sample_count - 400) { + envelope = (float)(sample_count - i) / 400.0f; + if (envelope < 0.0f) { + envelope = 0.0f; + } + } + samples[i] = (phase < 0.5f ? 0.15f : -0.15f) * envelope; + phase += phase_step; + if (phase >= 1.0f) { + phase -= 1.0f; + } + } + } + + SDL_QueueAudio(app->audio_device, samples, (Uint32)((size_t)sample_count * sizeof(float))); + free(samples); +} + +void app_play_intro_tune(App *app) +{ + int i; + + if (!app->audio_ok) { + return; + } + + SDL_ClearQueuedAudio(app->audio_device); + for (i = 0; intro_tune[i].pitch != 0 || intro_tune[i].duration != 0; ++i) { + queue_tone(app, intro_tune[i].pitch, intro_tune[i].duration); + } +} + +void app_stop_audio(App *app) +{ + if (app->audio_ok) { + SDL_ClearQueuedAudio(app->audio_device); + } +} + +bool app_load_win_image(App *app, const char *path, char *error, size_t error_size) +{ + FILE *fp = fopen(path, "rb"); + const int width = 400; + const int height = 400; + size_t count; + unsigned char *indices; + uint32_t *pixels; + int i; + + if (fp == NULL) { + snprintf(error, error_size, "could not open %s", path); + return false; + } + + indices = (unsigned char *)malloc((size_t)width * (size_t)height); + pixels = (uint32_t *)malloc((size_t)width * (size_t)height * sizeof(uint32_t)); + if (indices == NULL || pixels == NULL) { + free(indices); + free(pixels); + fclose(fp); + snprintf(error, error_size, "not enough memory for %s", path); + return false; + } + + count = fread(indices, 1, (size_t)width * (size_t)height, fp); + fclose(fp); + if (count != (size_t)width * (size_t)height) { + free(indices); + free(pixels); + snprintf(error, error_size, "unexpected size for %s", path); + return false; + } + + for (i = 0; i < width * height; ++i) { + SDL_Color color = ega_palette[indices[i] & 15]; + pixels[i] = ((uint32_t)255 << 24) | ((uint32_t)color.r << 16) | ((uint32_t)color.g << 8) | (uint32_t)color.b; + } + + app->win_texture = SDL_CreateTexture(app->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, width, height); + if (app->win_texture != NULL) { + SDL_UpdateTexture(app->win_texture, NULL, pixels, width * (int)sizeof(uint32_t)); + app->win_texture_width = width; + app->win_texture_height = height; + } + + free(indices); + free(pixels); + return app->win_texture != NULL; +} + +void app_render_instructions(App *app, const char *level_name) +{ + clear_screen(app, color_black); + draw_text(app, 21, 3, color_white, "ATTACK THE CLIFFS"); + draw_text(app, 17, 6, color_yellow, "PRESS ENTER TO START"); + draw_text(app, 19, 8, color_light_blue, "PRESS ESC TO QUIT"); + draw_text(app, 5, 11, color_white, "F1 = SELECT PETERB"); + draw_text(app, 5, 12, color_white, "F2 = SELECT DOUGALH"); + draw_text(app, 5, 13, color_white, "ARROWS = SELECT DIRECTION"); + draw_text(app, 5, 16, color_white, "LEFT HAND Q / A"); + draw_text(app, 5, 17, color_white, "LEFT FOOT S / X"); + draw_text(app, 5, 18, color_white, "RIGHT FOOT K / M"); + draw_text(app, 5, 19, color_white, "RIGHT HAND P / L"); + draw_text(app, 5, 22, color_white, "BELAY MAN W / B / O"); + draw_text(app, 5, 23, color_white, "BELAY LEDGE E / I"); + draw_text(app, 5, 26, color_white, "HIT MOVEMENT KEYS QUICKLY TO COMBINE MOVES."); + draw_text(app, 5, 27, color_white, "USE THE ROPE CREATIVELY."); + draw_text(app, 5, 29, color_yellow, "LEVEL:"); + draw_text(app, 13, 29, color_white, level_name); +} + +void app_render_game(App *app, const Game *game, uint64_t now_ms) +{ + int i; + + clear_screen(app, color_black); + draw_level_name(app, game->level.path); + + for (i = 0; i < game->level.count; ++i) { + draw_ledge(app, &game->level.ledges[i]); + } + + draw_rope(app, &game->rope); + draw_pitons(app, game->pitons); + draw_man(app, &game->men[0], game->active_man == 0 ? color_yellow : color_light_blue); + draw_man(app, &game->men[1], game->active_man == 1 ? color_yellow : color_light_blue); + draw_round_hud(app, game, now_ms); +} + +void app_render_result(App *app, const Game *game) +{ + SDL_Rect dst; + int line; + char buffer[64]; + int start_row = 24; + + clear_screen(app, color_black); + draw_text(app, 25, 3, color_white, game_result_title(game->result)); + + if (game->result == result_win && app->win_texture != NULL) { + dst.w = 320; + dst.h = 320; + dst.x = (screen_width - dst.w) / 2; + dst.y = 40; + SDL_RenderCopy(app->renderer, app->win_texture, NULL, &dst); + start_row = 47; + } + + for (line = 0; line < 4; ++line) { + draw_text(app, 4, start_row + line, line == 3 ? color_light_blue : color_white, game_result_line(game->result, line)); + } + + snprintf(buffer, sizeof(buffer), "TOTAL NUMBER OF MOVES = %d", game->move_count); + draw_text(app, 18, game->result == result_win ? 6 : 21, color_yellow, buffer); +} + +void app_present(App *app) +{ + SDL_RenderPresent(app->renderer); +} + +void app_render_hook(void *userdata, const Game *game) +{ + App *app = (App *)userdata; + + app_render_game(app, game, SDL_GetTicks64()); + app_present(app); +} + +void app_delay_hook(void *userdata, uint32_t ms) +{ + (void)userdata; + SDL_Delay(ms); +} diff --git a/src/sdl_app.h b/src/sdl_app.h new file mode 100644 index 0000000..f3a09a1 --- /dev/null +++ b/src/sdl_app.h @@ -0,0 +1,34 @@ +#ifndef SDL_APP_H +#define SDL_APP_H + +#include +#include +#include + +#include + +#include "cliffs.h" + +typedef struct { + SDL_Window *window; + SDL_Renderer *renderer; + SDL_AudioDeviceID audio_device; + bool audio_ok; + SDL_Texture *win_texture; + int win_texture_width; + int win_texture_height; +} App; + +bool app_init(App *app, bool hidden, char *error, size_t error_size); +void app_shutdown(App *app); +bool app_load_win_image(App *app, const char *path, char *error, size_t error_size); +void app_play_intro_tune(App *app); +void app_stop_audio(App *app); +void app_render_instructions(App *app, const char *level_name); +void app_render_game(App *app, const Game *game, uint64_t now_ms); +void app_render_result(App *app, const Game *game); +void app_present(App *app); +void app_render_hook(void *userdata, const Game *game); +void app_delay_hook(void *userdata, uint32_t ms); + +#endif