I like the idea, most of it's probably a case of getting used to it. I suspect you were hindered a lot by maintaining compatibility...but maybe we are best sticking to vanilla blitz. Who knows, it may even compile in blitzmax - how cool would that be for a first demo? ;-)
Anyway, here we go with tonights updates. Lots of them...
In editor\blitz.bb, at the top of the mouse input code, amend the main layer stuff to:
In engine.bb, remove these two separate bits of code
In consts.bb, I've moved the map and objectdef arrays in here from map.bb, plus type definitions
And I moved in the LAYER_VISIBLE global from map.bb for neatness
In editor.bp, add an include for "inc\consts.bb"
In map.bb, add the following to the load function between the MAP and OBJ loads
also, change all ReadInt or ReadStrings in OBJECT *load* and *save* code to ReadLine, and add the following new function
You'll need the new OBJ file, straight text for now, so I've just copied it in below: copy, paste and save as test.obj
In players.bb, add the following
and replace Player_updateall with this:
Finally, in hud.bb, drawhud function amend the select case criteria.
On that note, I think we need to worry about a 'proper' inventory sytstem, although I really don't like the idea of an RPG style inventory. To be discussed once we sort other stuff out methinks...
For reference, I've put together some notes on how I see scripting working below.
Hope that's everything! From now on, scripting changes should just be changes to the scripts held in the OBJ file, and RunScript (although it might well call other existing code), plus a couple of minor additions to other bits of code to support other triggers. But for now, this is it!
Anyway, here we go with tonights updates. Lots of them...
In editor\blitz.bb, at the top of the mouse input code, amend the main layer stuff to:
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
In engine.bb, remove these two separate bits of code
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
In consts.bb, I've moved the map and objectdef arrays in here from map.bb, plus type definitions
; 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
And I moved in the LAYER_VISIBLE global from map.bb for neatness
In editor.bp, add an include for "inc\consts.bb"
In map.bb, add the following to the load function between the MAP and OBJ loads
;load the object file For od.objdef=Each objdef Delete od Next
also, change all ReadInt or ReadStrings in OBJECT *load* and *save* code to ReadLine, and add the following new function
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
You'll need the new OBJ file, straight text for now, so I've just copied it in below: copy, paste and save as test.obj
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)
In players.bb, add the following
;-----------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
and replace Player_updateall with this:
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
Finally, in hud.bb, drawhud function amend the select case criteria.
Select Objectdef(pu\obj)\image Case 2,3,4,5: frame=Objectdef(pu\obj)\image Default: ok=False End Select
On that note, I think we need to worry about a 'proper' inventory sytstem, although I really don't like the idea of an RPG style inventory. To be discussed once we sort other stuff out methinks...
For reference, I've put together some notes on how I see scripting working below.
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
Hope that's everything! From now on, scripting changes should just be changes to the scripts held in the OBJ file, and RunScript (although it might well call other existing code), plus a couple of minor additions to other bits of code to support other triggers. But for now, this is it!