I like the progress so far!
I'm currently working on some music for this :)
I'm currently working on some music for this :)
; animation frame control If move pl\fpause = (pl\fpause+1) Mod 10 If pl\fpause = 0 Then pl\frame = (pl\frame+2) Mod 15 EndIf
Function dirdif(angle1,angle2) a= ((angle2 - angle1) Mod 16 + 24) Mod 16 - 8 If a<0 Then a=-1 If a>0 Then a=1 angle1 = angle1 + a If angle1>15 Then angle1=0 If angle1<0 Then angle1=15 Return angle1 End Functionto deal with 16 frames of rotation rather than 8.
;collision Layer If MouseDown(1) And activelayer = LayerCollision Then ;if left control is held If KeyDown(29) Then map(xpos,ypos,LayerCollision)=czVisStatic Else map(xpos,ypos,LayerCollision)=czStatic EndIf map(xpos,ypos,LayerHits)=-1 EndIf If MouseDown(3) And activelayer = LayerCollision Then ;if left control is held If KeyDown(29) Then map(xpos,ypos,LayerCollision)=czVisDynamic Else map(xpos,ypos,LayerCollision)=czDynamic EndIf map(xpos,ypos,LayerHits)= lhDefaultHits ;see consts.bb for lhDefaultHits EndIf
Function drawcollision(xpos,ypos) ; sort out the block location based on pixel co-ord xoff = Floor(xpos/32) yoff = Floor(ypos/32) For x=0 To 19 For y=0 To 14 Select map(x+xoff,y+yoff,0) Case czStatic Color 255,0,0 Rect (x*32)+7,(y*32)+7,18,18,False Case czDynamic Color 0,255,0 Rect (x*32)+7,(y*32)+7,18,18,False Case czVisStatic Color 255,255,255 Rect (x*32)+8,(y*32)+8,16,16,False Color 255,0,0 Rect (x*32)+7,(y*32)+7,18,18,False Case czVisDynamic Color 255,255,255 Rect (x*32)+8,(y*32)+8,16,16,False Color 0,255,0 Rect (x*32)+7,(y*32)+7,18,18,False Default ;do nothing End Select Next Next End Function
Const czVisStatic = 3 Const czVisDynamic = 4 ;multiples of 2 are dynamic
Function CheckVis(x,y) ; This function determine the map vis for a player ; add the start tile to the list v.vis_list=New vis_list v\x=x v\y=y ; and start checking by looping through all tiles in the list that need checking Repeat ; go through all 8 nearby squares For i=1 To 8 Select i Case 1:x=v\x-1:y=v\y-1 Case 2:x=v\x-1:y=v\y Case 3:x=v\x-1:y=v\y+1 Case 4:x=v\x:y=v\y-1 Case 5:x=v\x:y=v\y+1 Case 6:x=v\x+1:y=v\y-1 Case 7:x=v\x+1:y=v\y Case 8:x=v\x+1:y=v\y+1 End Select ; if it's on the map, we check it If x>=0 And x<=255 And y>=0 And y<=255 Then ; if it's never been checked before (vis=0), check it. otherwise, don't bother If map(x,y,LAYER_VISIBLE)=0 Then ; set it to visible (including all solid blocks at the boundary of vis are themselves visible) map(x,y,LAYER_VISIBLE)=1 ; and provided it's not a vis stop, add it to the list of tiles to process If map(x,y,0)<czVisStatic Then nv.vis_list=New vis_list nv\x=x nv\y=y End If End If End If Next ; set current tile to visible map(v\x,v\y,LAYER_VISIBLE)=1 ; eliminate this one from the list to check and move on to the next one Delete v v=First vis_list Until v=Null ; now check all aliens (UNSURE if this should be here, or in aliens.bb?) For al.alien = Each alien If al\visible=0 Then If gettile(al\x,al\y,LAYER_VISIBLE)>0 Then al\visible=1 End If Next End Function
Default Map(x,y,LayerObject) = 0 Map(x,y,LayerCollision) = 0 CheckVis(x,y)
Select Result Case czStatic,czVisStatic ;bullet hit wall ok = False Case czDynamic,czVisDynamic ;bullet hit destructible collision zone ok = False DamageTile(b\x,b\y,1);change to weapon damage so that different weapons will damage faster or slower? End Select
If x < 0 Then Return If y < 0 Then Return If x > 7552 Then Return If y > 7552 Then Return
; collect objects If gettile(pl\x,pl\y,2)>0 ;Determine what the player has collected ; Don't add ammo to the collected list objcollect = gettile(pl\x,pl\y,2) collectable = True Select objcollect Case 1 ;Ammo givePlayerAmmo(pl,15) playoneshot(clip_pickup,pl\x,pl\y) ;Change once we get a proper sfx collectable = False Case 2 ;Health Player_GiveHealth(pl,15) playoneshot(health_pickup,pl\x,pl\y) Case 3,4,5,6 playoneshot(key_pickup,pl\x,pl\y) Case 9 collectable = False End Select puttile(pl\x,pl\y,2,0) If collectable p.pickedup = New pickedup p\player = pl\id ; allowing for multiple players p\obj = objcollect EndIf EndIf
Function Player_GiveHealth(p.player, h#) p\health = p\health + h If p\health > 100 Then p\health = 100 End If End Function
If Firing=True And (pl\clip>=0 And pl\ammo>0) Then If MilliSecs()>Pl\IsFiringTimer Pl\IsFiringTimer=MilliSecs()+25 pl\IsFiring = Not pl\IsFiring EndIf Else Pl\IsFiring=False EndIf
If control(pright,pl\id) And pl\x<7552 Then mr=True If control(pleft,pl\id) And pl\x>0 Then ml=True If control(pup,pl\id) And pl\y>0 Then mu=True If control(pdown,pl\id) And pl\y<7552 Then md=True If control(pfire1,pl\id) fireWeapon(pl,pl\dir*45) firing = True EndIf
; Object Collision (walls, doors etc) If mr Then If gettile(pl\x+playerradius,pl\y,0)=0 Then If control(pstrafe,Player_Count+1)=0 Then pl\x=pl\x+1 move=True ElseIf gettile(pl\x+playerradius,pl\y,2)=7 Then If keycheck(pl\id) Then opendoor((pl\x+playerradius)/32,pl\y/32) End If direct=direct+"R" End If If ml Then If gettile(pl\x-playerradius,pl\y,0)=0 Then If control(pstrafe,Player_Count+1)=0 Then pl\x=pl\x-1 move=True ElseIf gettile(pl\x-playerradius,pl\y,2)=7 Then If keycheck(pl\id) Then opendoor((pl\x-playerradius)/32,pl\y/32) End If direct=direct+"L" End If If mu Then If gettile(pl\x,pl\y-playerradius,0)=0 Then If control(pstrafe,Player_Count+1)=0 Then pl\y=pl\y-1 move=True ElseIf gettile(pl\x,pl\y-playerradius,2)=8 Then If keycheck(pl\id) Then opendoor(pl\x/32,(pl\y-playerradius)/32) End If direct=direct+"U" End If If md Then If gettile(pl\x,pl\y+playerradius,0)=0 Then If control(pstrafe,Player_Count+1)=0 Then pl\y=pl\y+1 move=True ElseIf gettile(pl\x,pl\y+playerradius,2)=8 Then If keycheck(pl\id) Then opendoor(pl\x/32,(pl\y+playerradius)/32) End If direct=direct+"D" End If
; animation frame control If move al\fpause = (al\fpause+1) Mod 5 If al\fpause = 0 Then al\frame = (al\frame+1) Mod al\frameTotal End If EndIf
al\kind = kind Select kind Case 0: ; standard alien al\radius=20 al\offset=32 al\frameTotal = 1 Case 1: ; face hugger al\radius=20 al\offset=16 al\frameTotal = 2 End Select
Function load() FlushKeys() SaveWin = CreateImage(GraphicsWidth(), GraphicsHeight()) oldbuffer = GraphicsBuffer() SetBuffer ImageBuffer(SaveWin) Print Print Print Print Print Color 255,255,255 FileName$ = Input$("Enter filename to load (without .map extension): ") FileName$ = FileName$ If FileType(FileName$+".map") = 1 filein = ReadFile(FileName$+".map") For layer = 0 To NumLayers For x=0 To 255 For y=0 To 255 map(x,y,layer)= ReadInt(filein) Next Next Next CloseFile filein Else DebugLog "Filename does not exist: " + FileName$+".map" EndIf If FileType(FileName$+".obj") = 1 filein = ReadFile(FileName$+".obj") NumObjects=ReadInt(filein) If NumObjects>0 Then Dim objectdef.objdef(NumObjects) For i=1 To NumObjects objectdef(i)=New objdef objectdef(i)\image=ReadInt(filein) objectdef(i)\trigger=ReadInt(filein) objectdef(i)\name=ReadString(filein) objectdef(i)\script=ReadString(filein) Next End If CloseFile filein Else DebugLog "Filename does not exist: " + FileName$+".obj" EndIf DebugLog "Loaded: " + FileName$+".map AND "+filename$+".obj" FreeImage SaveWin SetBuffer oldbuffer End Function
Function save() FlushKeys() SaveWin = CreateImage(GraphicsWidth(), GraphicsHeight()) oldbuffer = GraphicsBuffer() SetBuffer ImageBuffer(SaveWin) Print Print Print Print Print Color 255,255,255 FileName$ = Input$("Enter filename to load (without .map extension): ") FileName$ = FileName$ fileout = WriteFile(FileName+".map") For layer = 0 To NumLayers For x=0 To 255 For y=0 To 255 WriteInt(fileout,map(x,y,layer)) Next Next Next CloseFile fileout ; save the object definition file fileout = WriteFile(FileName+".obj") WriteInt fileout,NumObjects ; number of objects For i=1 To NumObjects WriteInt fileout, objectdef(i)\image WriteInt fileout, objectdef(i)\trigger WriteString fileout, objectdef(i)\name WriteString fileout, objectdef(i)\script Next CloseFile fileout DebugLog "saved: " + FileName$+".map AND "+filename$+".obj" FreeImage SaveWin SetBuffer oldbuffer End Function
If layer = LayerObject For x = 0 To 19 For y = 0 To 14 If map(x+xoff,y+yoff,layer)>0 Then DrawImage objects,(x*32),(y*32),objectdef(map(x+xoff,y+yoff,layer))\image Next Next EndIf
Type objdef Field image% Field name$ Field trigger% ; Field script$ End Type Global NumObjects% Dim objectdef.objdef(NumObjects)
If xpos>-1 If MouseDown(1) And (activelayer<>LayerCollision And activeLayer <> LayerHits) Then If activelayer=LayerObject Then If activetile<NumObjects Then map(xpos,ypos,activelayer)=activetile+1 Else map(xpos,ypos,activelayer)=activetile+1 End If End If
Dim objectname$(9)and
; object names Data "Ammo","Health","Generic Key","Red Key","Green Key","Blue Key","V Door","H Door","Alien Blood" For n=1 To 9 Read objectname(n) Nextand add ClearVis() after the load() at the top
; TYPES and ARRAYS Type scriptitem Field command$ Field param$ End Type Type objdef Field mx,my ; map coords Field image% Field name$ Field trigger% ; see constants below Field script$ End Type Global NumObjects% Dim objectdef.objdef(NumObjects) Dim map(255,255,NumLayers+1) Const ScriptTrigger_OnOver=1 Const ScriptTrigger_OnTouch=2
;load the object file For od.objdef=Each objdef Delete od Next
Function ClearVis() ;set initial visibility for all tiles to none at all ;we'll set initial player vis when we create the player For x=0 To 255 For y=0 To 255 map(x,y,LAYER_VISIBLE)=0 Next Next End Function
11 0 1 Ammo addammo(50)|remove(me) 1 1 Health addhealth(10)|remove(me) 2 1 Key (grey) addkey(1)|remove(me) 3 1 Key (red) addkey(2)|remove(me) 4 1 Key (green) addkey(3)|remove(me) 5 1 Key (blue) addkey(4)|remove(me) 6 2 Door checkkey()|opendoor()|remove(nearby) 7 2 Door checkkey()|opendoor()|remove(nearby) 8 1 100 Credit addcredit(100)|remove(me) 9 1 50 Credit addcredit(50)|remove(me) 10 1 10 Credit addcredit(10)|remove(me)
;-----------scripting stuff Function RunScript(pl.player,obj.objdef,x,y) si.scriptitem=ParseScript(obj\script) While si<>Null Select si\command Case "addammo" givePlayerAmmo(pl,Int(si\param$)) Case "opendoor" opendoor(x/32,y/32) Case "checkkey" If Not keycheck(pl\id) Then Return Case "addkey" p.pickedup = New pickedup p\player = pl\id ; allowing for multiple players p\obj=Int(si\param)+2 playoneshot(key_pickup,x,y) Case "addcredit" ; TODO: Credits! Case "addhealth" playoneshot(health_pickup,x,y) p.pickedup = New pickedup p\player = pl\id ; allowing for multiple players p\obj = 2 Case "remove" If si\param="me" Then puttile(x,y,LayerObject,0) Default:RuntimeError "Unrecognised command : "+si\command End Select nsi.scriptitem=After si Delete si si=nsi Wend End Function Function ParseScript.scriptitem(s$) s$=Trim(s$) p=Instr(s$,"|") p2=Instr(s$,"(") si.scriptitem=New scriptitem fsi.scriptitem=si While p>0 If p2>p Or p2=0 Then RuntimeError "ERROR: Malformed script: "+s$ si\command=Left(s$,p2-1) si\param=Mid(s$,p2+1,p-p2-2) s$=Trim(Mid(s$,p+1)) si=New scriptitem p=Instr(s$,"|") p2=Instr(s$,"(") Wend si\command=Left(s$,p2-1) si\param=Mid(s$,p2+1,Len(s$)-p2-1) Return fsi End Function
Function Player_UpdateAll() ScreenX=0 ScreenY=0 Player_Count=0 For pl.player = Each player move=False direct$="" mr=False ml=False mu=False md=False firing = False ; get input If control(pright,pl\id) And pl\x<7552 Then mr=True If control(pleft,pl\id) And pl\x>0 Then ml=True If control(pup,pl\id) And pl\y>0 Then mu=True If control(pdown,pl\id) And pl\y<7552 Then md=True If control(pfire1,pl\id) fireWeapon(pl,pl\dir*45) firing = True EndIf If Firing=True And (pl\clip>=0 And pl\ammo>0) Then If MilliSecs()>Pl\IsFiringTimer Pl\IsFiringTimer=MilliSecs()+25 pl\IsFiring = Not pl\IsFiring EndIf Else Pl\IsFiring=False EndIf ; restrict players to the visible screen If pl\x-GOffsetX > GraphicsWidth()-32 And mr=True Then mr=False If pl\x-GOffsetX < 32 And ml=True Then ml=False If pl\y-GOffsety > GraphicsHeight()-32 And md=True Then md=False If pl\y-GOffsety < 32 And mu=True Then mu=False ; Object Collision (walls, doors etc) If mr Then If gettile(pl\x+playerradius,pl\y,0)=0 Then If control(pstrafe,Player_Count+1)=0 Then pl\x=pl\x+1 move=True EndIf tile=map((pl\x+playerradius)/32,pl\y/32,LayerObject) If tile>0 Then If objectdef(tile)\trigger=ScriptTrigger_OnTouch Then RunScript(pl,objectdef(tile),pl\x+playerradius,pl\y) End If direct=direct+"R" End If If ml Then If gettile(pl\x-playerradius,pl\y,0)=0 Then If control(pstrafe,Player_Count+1)=0 Then pl\x=pl\x-1 move=True End If tile=map((pl\x-playerradius)/32,pl\y/32,LayerObject) If tile>0 Then If objectdef(tile)\trigger=ScriptTrigger_OnTouch Then RunScript(pl,objectdef(tile),pl\x-playerradius,pl\y) End If direct=direct+"L" End If If mu Then If gettile(pl\x,pl\y-playerradius,0)=0 Then If control(pstrafe,Player_Count+1)=0 Then pl\y=pl\y-1 move=True End If tile=map(pl\x/32,(pl\y-playerradius)/32,LayerObject) If tile>0 Then If objectdef(tile)\trigger=ScriptTrigger_OnTouch Then RunScript(pl,objectdef(tile),pl\x,pl\y-playerradius) End If direct=direct+"U" End If If md Then If gettile(pl\x,pl\y+playerradius,0)=0 Then If control(pstrafe,Player_Count+1)=0 Then pl\y=pl\y+1 move=True End If tile=map(pl\x/32,(pl\y+playerradius)/32,LayerObject) If tile>0 Then If objectdef(tile)\trigger=ScriptTrigger_OnTouch Then RunScript(pl,objectdef(tile),pl\x,pl\y+playerradius) End If direct=direct+"D" End If ; if you're not firing change the direction you're looking If Not firing If direct = "U" Then pl\targetdir=0 If direct = "RU" Then pl\targetdir =1 If direct = "R" Then pl\targetdir =2 If direct = "RD" Then pl\targetdir = 3 If direct = "D" Then pl\targetdir = 4 If direct = "LD" Then pl\targetdir = 5 If direct = "L" Then pl\targetdir = 6 If direct = "LU" Then pl\targetdir = 7 If turndelay = 0 pl\dir = dirdif (pl\dir,pl\targetdir) EndIf EndIf ScreenX = ScreenX + pl\x ScreenY = ScreenY + pl\y Player_Count = Player_Count+1 ; collect objects If map(pl\x/32,pl\y/32,LayerObject)>0 Then If objectdef(map(pl\x/32,pl\y/32,LayerObject))\trigger=ScriptTrigger_OnOver Then RunScript(pl,objectdef(map(pl\x/32,pl\y/32,LayerObject)),pl\x,pl\y) End If If pl\health = 0 Then ;TODO: Death of player End If Next End Function
Select Objectdef(pu\obj)\image Case 2,3,4,5: frame=Objectdef(pu\obj)\image Default: ok=False End Select
Scripting Design Notes ====================== Each map has a <mapname>.obj file that is simply a series of strings. This can be tidied up later on, but allows us to edit objects / scripts without actually having a script editor. An object is defined once, and has a unique id (defined by it's position in the OBJ file. This ID is then held in the object layer of the map. Many locations on the map may reuse the same ID for common objects such as keys, cash, ammo, doors, etc. These share the same object definition, but the script can be run from many places on the map. Special objects (such as conversations) are defined in the same way, but are generally used only once (although you might have a conversation that you wish to be triggered by coming near a character, which might require several surrounding map tiles to point to the same object, but the object then does a remove(all) to ensure it's not used again). The idea is that if you want to add a new scripted object / event, you simply update the OBJ file (by hand for now) with a new entry (don't forget to up the count!), write the script in that entry, then load the editor, place the object somewhere (it should just appear in the palette), save and run. Once the scripting language is complete, that really is all you'll need to do - well, other than draw an image for it. Note that an image of 0 _should_ in principle be invisible, but I haven't tested it yet... (may be useful for conversations / traps / scripted events occurring on entering a room). FORMAT ====== Number of Objects, followed by the following fields repeated for each object: Image - the image in object_tiles that will be used for display Trigger - what will trigger the script to run (see below) description - a simple description of the object! script - The script to run TRIGGERS ======== trigger is one of: 1 - on passing over (collectable items, traps) 2 - on touching (doors, buttons on walls, intex systems) SCRIPTS ======= script is formed of a series of commands, all taking the format: commandname(params)|commandname(params) for example: addammo(100)|remove(me) Script commands implemented: addammo(N) - adds N to the players ammo count checkkey() - checks that they have a key, if not, the script stops opendoor() - runs the old opendoor code as is addkey(N) - adds key type N addhealth(N) - adds N health remove(me) - removes the object that caused this script to run TODO ==== CheckKey to look for a specified key type addcredit(N) - adds N credits remove(nearby) - removes all nearby objects of the same type (e.g. doors) and change opendoor? remove(all) - removes all objects of the same type playsoundonce(sfx) - play a specified sound effect and move sfx for health / keys into scripts playsoundcont(sfx) - play a continuous sound effect. No idea how we stop these though... showdoc(text) - show a document on screen showconv(x,y,text) - show a speech bubble at specified x,y. createalien(x,y,type,anim) - create an alien - need creation animations setcollision(x,y,value) - set the collision layer of the map directly setobject(x,y,value) - set the object layer of the map directly settile(x,y,value) - set the base tile layer of the map directly other triggers, such as onevery(N) for sounds, anims and alien spawn points
For n=1 To 20 Repeat x=Rand(100,2000) y=Rand(100,2000) Until gettile(x,y,0)=0 spawnalien(x,y,n,Rand(FACE_HUGGER_ALIEN,STANDARD_ALIEN)) Next ; **
Function Alien_DrawAll() Local al.alien For al.alien = Each alien If al\visible Then DrawImage al\image,al\x-al\offset-screenx,al\y-al\offset-screeny,(al\dir * al\frameTotal) +al\frame End If Next End Function