unit GameTutorial3; {$mode objfpc}{$H+} //step3: //a platformer where everything is 1 hit kill //Warning: While it's tempting to read through the code to find out how to beat this, try it first without interface uses windows, Classes, SysUtils, gamepanel, guitextobject, staticguiobject, gamebase, target, bullet,Dialogs, Graphics, playerwithhealth, math, gl, glext, glu, gamecube, platformenemy, platformplayer; type Tdoor=class(TObject) private w: single; h: single; fx,fy: single; flocked: boolean; door: TStaticGUIObject; lock: TStaticGUIObject; public function isinside(_x,_y: single): boolean; procedure render; constructor create; destructor destroy; override; property locked: boolean read flocked write flocked; property x: single read fx write fx; property y: single read fy write fy; end; TGame3=class(TGameBase) private fpanel: Tgamepanel; pplayer: TPlatformPlayer; platforms: array of TgameCube; enemies: array of TPlatformEnemy; //change from gamecube to gameobject after testing info: TGUITextObject; infobutton: TStaticGUIObject; pausescreen: TGUITextObject; pausebutton: TStaticGUIObject; door: Tdoor; gametime: qword; walkdirection: single; jumpkeydown: boolean; yvelocity: single; falling: boolean; fuckplayer: boolean; //when true the platformenemies will move to form a barrier around the door fuckplayertime: qword; procedure fuckplayercode; //code to call each gametick when the game has been won function pauseclick(sender: tobject): boolean; function infoPopup(sender: tobject): boolean; function HideInfo(sender: tobject): boolean; procedure spawnPlayer; procedure resetgame; public greencount: integer; procedure gametick(currentTime: qword; diff: integer); override; procedure render; override; function KeyHandler(TGamePanel: TObject; keventtype: integer; Key: Word; Shift: TShiftState):boolean; override; constructor create(p: TGamePanel); destructor destroy; override; end; implementation uses registry; function TDoor.isinside(_x,_y: single): boolean; begin result:=(_x>x - w/2) and (_xy - h/2) and (_y=0 then walkdirection:=-0.1; //starts with a lot of inertia VK_RIGHT,VK_D: if walkdirection<=0 then walkdirection:=0.1; VK_SPACE, VK_W,VK_UP: begin jumpkeydown:=true; //makes mountaingoating easier if falling=false then begin yvelocity:=1.45; end; end; end; end else begin case key of VK_LEFT,VK_A: if walkdirection<0 then walkdirection:=0; VK_RIGHT,VK_D: if walkdirection>0 then walkdirection:=0; VK_SPACE,VK_W,VK_UP: begin jumpkeydown:=false; end; end; end; result:=false; end; procedure TGame3.render; var i: integer; begin for i:=0 to length(platforms)-1 do platforms[i].render; if info<>nil then info.render else begin if infobutton<>nil then infobutton.render; end; pausebutton.render; if pausescreen<>nil then pausescreen.render; if pplayer<>nil then pplayer.render; for i:=0 to length(enemies)-1 do begin if enemies[i]<>nil then enemies[i].render; end; if door<>nil then door.render; end; procedure TGame3.fuckplayercode; //in 2 seconds take positions var t: qword; pos: single; targetx, targety: single; distance: single; begin t:=gametime-fuckplayertime; pos:=t/2000; if length(enemies)>=3 then begin if enemies[0]<>nil then begin //move it to the bottom left rotated 45 degress to the left //take 2 seconds to rotate targetx:=door.x-(door.w/2)-(enemies[0].width/2); if pos>=1 then begin enemies[0].rotation:=360-90; enemies[0].x:=targetx; end else begin enemies[0].rotation:=360-(90*pos); distance:=targetx-enemies[0].x; enemies[0].x:=enemies[0].x+distance*pos; end; end; if enemies[1]<>nil then begin //move it to the topleft rotated 45 degrees to the left targetx:=door.x-(door.w/2)-(enemies[0].width/2); targety:=enemies[0].y-(enemies[0].height/2)-(enemies[1].height/2); if pos>=1 then begin enemies[1].rotation:=360-90; enemies[1].x:=targetx; enemies[1].y:=targety; end else begin enemies[1].rotation:=360-(90*pos); distance:=targetx-enemies[1].x; enemies[1].x:=enemies[1].x+distance*pos; distance:=targety-enemies[1].y; enemies[1].y:=enemies[1].y+distance*pos; end; end; if enemies[2]<>nil then begin //move it to the topright targetx:=door.x; targety:=door.y-(door.h/2)-(enemies[2].width/2); if pos>=1 then begin enemies[2].x:=targetx; enemies[2].y:=targety; end else begin distance:=targetx-enemies[2].x; enemies[2].x:=enemies[2].x+distance*pos; distance:=targety-enemies[2].y; enemies[2].y:=enemies[2].y+distance*pos; end; end; end; end; procedure TGame3.gametick(currentTime: qword; diff: integer); var i,j: integer; oldx, oldy: single; newx, newy: single; oldplayerfeet, newplayerfeet: single; platformwalky: single; begin if ticking=false then exit; if pausescreen<>nil then exit; inc(gametime,diff); oldx:=pplayer.x; oldy:=pplayer.y; newy:=pplayer.y-(yvelocity*(diff/1000)); oldplayerfeet:=oldy+pplayer.height/2; newplayerfeet:=newy+pplayer.height/2; pplayer.y:=newy; if yvelocity<0 then begin //falling down //check the platforms that are between oldy and pplayer.y for i:=0 to length(platforms)-1 do begin platformwalky:=platforms[i].y-(platforms[i].height/2); if (oldplayerfeet<=platformwalky) and (newplayerfeet>=platformwalky) then begin //it passed this platform. Check if the width is good enough if (pplayer.x>platforms[i].x-platforms[i].width/2) and (pplayer.x0 then falling:=true; if jumpkeydown and not falling then begin yvelocity:=1.45 end else yvelocity:=yvelocity-0.1; if walkdirection<>0 then walkdirection:=walkdirection*1.035; if walkdirection<-0.7 then walkdirection:=-0.7; if walkdirection>0.7 then walkdirection:=0.7; if (pplayer<>nil) and (walkdirection<>0) then begin pplayer.x:=pplayer.x+(walkdirection*(diff/1000)); end; if pplayer.x>(1-pplayer.width/2) then pplayer.x:=(1-pplayer.width/2); if pplayer.x<(-1+pplayer.width/2) then pplayer.x:=(-1+pplayer.width/2); if fuckplayer then fuckplayercode else begin for i:=0 to length(enemies)-1 do begin if enemies[i]<>nil then enemies[i].travel(diff); end; end; for i:=0 to length(enemies)-1 do begin if (enemies[i]<>nil) then begin if (not enemies[i].exploding) and enemies[i].checkCollision(pplayer) then pplayer.explode; if enemies[i].blownup then freeandnil(enemies[i]); end; end; if (door.locked) and (greencount>=length(platforms)) then begin door.locked:=false; fuckplayer:=true; //what happens when you nop this out ? fuckplayertime:=gametime; //another point of attack end; //I 'could' do a constant check for door.unlocked and then enter the fuckplayer mode, but just being nice and making it possible to unlock the door without having to mark all green if (pplayer<>nil) and (not pplayer.exploding) and (door.locked=false) and door.isinside(pplayer.x,pplayer.y) then begin ticking:=false; showmessage('well done'); with tregistry.create do begin if OpenKey('\Software\Cheat Engine\GTutorial', true) then WriteBool('This does not count as a solution for tutorial 3',True); free; end; gamewon; end; if (pplayer<>nil) and (pplayer.blownup) then begin //recreate the player //game over, restart freeandnil(pplayer); resetgame; end; end; function TGame3.pauseclick(sender: tobject): boolean; begin if pausescreen<>nil then freeandnil(pausescreen) else begin pausescreen:=TGUITextObject.create(fpanel); pausescreen.firstTextBecomesMinWidth:=true; pausescreen.width:=0.8; pausescreen.height:=0.8; pausescreen.x:=0; pausescreen.y:=0; pausescreen.rotationpoint.x:=0; pausescreen.rotationpoint.y:=0; pausescreen.color:=clRED; pausescreen.bcolor:=clWhite; pausescreen.font.Size:=18; pausescreen.text:='!PAUSED!'; end; result:=true; end; function TGame3.infoPopup(sender: tobject): boolean; begin if info<>nil then exit(false); info:=TGUITextObject.create(fpanel); info.firstTextBecomesMinWidth:=true; info.width:=0.8; info.height:=0.8; info.x:=0; info.y:=0; info.rotationpoint.x:=0; info.rotationpoint.y:=0; info.color:=clBlack; info.bcolor:=clWhite; info.backgroundAlpha:=190; info.font.Size:=9; info.text:='Step 3:'#13#10+ 'Mark all platforms green to'#13#10+ 'unlock the door'#13#10+ ' '#13#10+ 'Beware: Enemies are 1 hit kill'#13#10+ ' (and bad losers)'#13#10+ ' '#13#10+ ' '#13#10+ 'Have fun!'#13#10+ ' '#13#10+ 'Hint: There are multiple solutions'#13#10+ ' e.g: Find collisiton detect with '#13#10+ ' enemies, or teleport, or fly'#13#10+ ' or ....'#13#10; info.OnClick:=@HideInfo; result:=true; end; function TGame3.HideInfo(sender: tobject): boolean; begin freeandnil(info); result:=true; end; procedure TGame3.spawnPlayer; begin if pplayer=nil then pplayer:=tplatformplayer.create; //temporary holder pplayer.x:=-1+0.1; pplayer.y:=1-0.05-0.15/2; end; procedure TGame3.resetgame; var i: integer; begin spawnplayer; if length(platforms)<12 then setlength(platforms,12); for i:=0 to length(platforms)-1 do begin if platforms[i]=nil then platforms[i]:=Tgamecube.create; platforms[i].color.b:=0; platforms[i].color.r:=0.8; platforms[i].color.g:=0; end; //ground platforms[0].width:=2; platforms[0].height:=0.05; platforms[0].x:=0; platforms[0].y:=0.975; //p1 platforms[1].width:=0.3; platforms[1].height:=0.05; platforms[1].x:=-0.4; platforms[1].y:=0.7; platforms[2].width:=0.3; platforms[2].height:=0.05; platforms[2].x:=0.1; platforms[2].y:=0.7; platforms[3].width:=0.2; platforms[3].height:=0.05; platforms[3].x:=0; platforms[3].y:=0.42; platforms[4].width:=0.2; platforms[4].height:=0.05; platforms[4].x:=0.4; platforms[4].y:=0.2; platforms[5].width:=0.2; platforms[5].height:=0.05; platforms[5].x:=0.6; platforms[5].y:=-0.05; platforms[6].width:=0.4; platforms[6].height:=0.05; platforms[6].x:=0.75; platforms[6].y:=-0.3; platforms[7].width:=0.1; platforms[7].height:=0.05; platforms[7].x:=0.0; platforms[7].y:=-0.3; platforms[8].width:=0.4; platforms[8].height:=0.05; platforms[8].x:=-0.3; platforms[8].y:=-0.5; platforms[9].width:=0.2; platforms[9].height:=0.05; platforms[9].x:=-0.4; platforms[9].y:=-0.7; platforms[10].width:=0.2; platforms[10].height:=0.05; platforms[10].x:=-0.8; platforms[10].y:=-0.7; platforms[11].width:=0.1; platforms[11].height:=0.05; platforms[11].x:=-0.8; platforms[11].y:=0; fuckplayer:=false; if length(enemies)<3 then setlength(enemies,3); if enemies[0]=nil then enemies[0]:=TPlatformEnemy.create; enemies[0].x:=-0.5; enemies[0].y:=1-enemies[0].height/2-platforms[0].height; enemies[0].minx:=-0.6; enemies[0].maxx:=0-enemies[0].width/2; enemies[0].rotation:=0; if enemies[1]=nil then enemies[1]:=TPlatformEnemy.create; enemies[1].x:=0+enemies[0].width/2; enemies[1].y:=1-enemies[1].height/2-platforms[2].height; enemies[1].minx:=0; enemies[1].maxx:=0.9; enemies[1].rotation:=0; if enemies[2]=nil then enemies[2]:=TPlatformEnemy.create; enemies[2].x:=0+enemies[0].width/2; enemies[2].y:=platforms[8].y-(enemies[2].height/2)-(platforms[8].height/2); enemies[2].minx:=platforms[8].x-platforms[8].width/2; enemies[2].maxx:=platforms[8].x+platforms[8].width/2; enemies[2].speed:=enemies[1].speed/2; //got to keep the illusion the player can potentially win greencount:=0; door.locked:=true; end; destructor TGame3.destroy; begin if pplayer<>nil then freeandnil(pplayer); if infobutton<>nil then freeandnil(infobutton); if info<>nil then freeandnil(info); inherited destroy; end; constructor TGame3.create(p: TGamePanel); var i: integer; begin fpanel:=p; infobutton:=TStaticGUIObject.create(p,'infobutton.png',0.1,0.1); infobutton.rotationpoint.y:=1; //so the bottom will be the y pos infobutton.x:=-1; infobutton.y:=1-0.05; infobutton.OnClick:=@infopopup; pausebutton:=TStaticGUIObject.create(p,'pausebutton.png',0.1,0.1); pausebutton.rotationpoint.y:=1; pausebutton.x:=-1+0.1; pausebutton.y:=1-0.05; pausebutton.OnClick:=@pauseclick; door:=Tdoor.create; resetgame; infopopup(infobutton); ticking:=true; { p.background.r:=0; p.background.g:=0; p.background.b:=1;} end; end.