CP - Alien Breed Remake - 7

Miscellaneous Forums/Blitz Showcase/CP - Alien Breed Remake - 7




Download site

On-Line Todo list

Hi Rob, Topic 7 already. Lucky number 7. :)

I would help but unfortunately I've got a lot on at the moment. But make sure you keep on going in the meantime. Or else :)

I understand about the players gun facing in the direction you're shooting, but at the moment, it's just not right with the body all twisted. It looks like you're running sideways. Time to just use some more memory and have a running direction and a firing stance. Or just follow the original AB and have the character and gun looking straight ahead at all times.

Also, new hud. Not a fan. That's not an Alien Breed hud, which it was before. (although I do have a soft spot for the flying bullets, that's just because I'm a sucker for silly effects :)).

Agree with the player direction / firing direction thing - it just doesn't look quite right at the moment. The time to fix this is probably at the same time as having separate gun graphics to player graphics for each weapon. By having the separate, the player animation can wobbly side to side as you walk, but the gun always points in one of 8 directions.

I think I also agree on the HUD. I think it looks too WW1-ish. Or Cowboy-style. I do think the old one needed work, but I'm not sure this is quite right for an alien breed module. Although it may be right for some other module...

Regarding the shooting / direction / running thing... a thought...
It would mean doubling the amount of graphics however...
How about a running set of animations (face on) and a shooting (side on) and when you're shooting you can only move at 70% of your regular speed? or something...

Hud: I don't like it either, and I don't like the flying bullets, I think Rims was just running on sleep deprevation when he came up with it!

I've been working on creating sounds at the moment, and organising how the sounds will work, how they're attached to aliens / players etc etc. And maybe some better sound management.

I'm playing with the editor, purely user-friendliness for now, to avoid having a complex manual. Just a quick update for now though. Replace the arrow key scrolling in editor.bb with the below to have the mouse do the same job on the edges of the map. Very handy for drawing long walls or big rooms...
	
	; map movement code (arrow keys)
	key_left=False:key_right=False:key_up=False:key_down=False	
	If (MouseX()<640 And MouseY()<480) Then 
		MouseOverMap = True 
		If MouseX()<5 Then key_left=True
		If MouseX()>635 Then key_right=True
		If MouseY()<5 Then key_up=True
		If MouseY()>475 Then key_down=True		
		If key_left Or key_right Or key_up Or key_down Then
			If Not mousescroll Then mousescroll=True:scrolldelay=setscrolldelay
		Else
			mousescroll=False
		End If
	Else
		MouseOverMap = False
	EndIf

	If KeyDown(205) Then key_right=True
	If KeyDown(203) Then key_left=True
	If KeyDown(200) Then key_up=True
	If KeyDown(208) Then key_down=True
	
	; slow down the scrolling a bit
	If scrolldelay < 0
		If key_right And sc_x<7552 Then sc_x=sc_x+32 : scrolldelay = setscrolldelay
		If key_left And sc_x>0 Then sc_x=sc_x-32 : scrolldelay = setscrolldelay
		If key_up And sc_y>0 Then sc_y=sc_y-32 : scrolldelay = setscrolldelay
		If key_down And sc_y<7552 Then sc_y=sc_y+32 : scrolldelay = setscrolldelay
	Else
		scrolldelay = scrolldelay - 1
	EndIf

Also, for better tooltips, replace (and add) the following to gui.bb
;;;;;;;;;;;;;;;;;;;;
; FUNCTION ToolTip ;
;;;;;;;;;;;;;;;;;;;;

Global tt_txt$,tt_x,tt_y,tt_w,tt_h

Function ToolTip(x,y,txt$)
	tt_txt$=txt$
	tt_w=StringWidth(txt$)+4
	tt_h=FontHeight()+4
	If x+tt_w+4>GraphicsWidth() Then tt_x=GraphicsWidth()-tt_w Else tt_x=x
	If y+tt_h+4>GraphicsHeight() Then tt_y=GraphicsHeight()-tt_h Else tt_y=y
End Function

Function DrawToolTip()
	If tt_txt$<>"" Then
		Color 255,255,225
		Rect tt_x,tt_y,tt_w,tt_h,True
		Color 0,0,0
		Rect tt_x,tt_y,tt_w,tt_h,False
		Text tt_x+2,tt_y+2,tt_txt$
		tt_txt$=""
	End If
End Function

And add DrawToolTip() just before all Flips.

Nothing uses this as yet, but I intend to add some to the main editor screen...

Actually, the listbox component uses tooltips when an item is wider than the listbox.

I've just had a play with animation, the firing/running thing looks really good, however, it does mean each character has a 1024x1024 anim image... does this sound too much?

Er, this game is looking really good now...

Just thought I should say that :)

1024x1024 doesn't sound too much to me, but it might be an issue for low spec machines.

I agree with Rob regarding the HUD, it just doesn't look right. Having all your immediately important info in one easy to look at place is much better.

Zips Updated... Quite a bit of stuff done...

17-10-04 :
Rob:
- Aliens now have a class.txt file attached. All alien stats are loaded from this file, also aliens are only loaded if they get spawned. Changes to Alien.bb, engine.bb, and initialise.bb
- Created more sound fx, added a few to gun noises and stuff
- Started with crappy hard coding of different sounds to aliens, the sounds need a lot of work
- Added footstep sounds
- Updated player graphics so there are now run and shoot animations, when shooting you travel at 80% speed
- Updated the playoneshot function to take channels and pitch into account


I need to spend a fair bit of time sorting the sound out, we've let this one slide a bit too long and it's going to be a major overhaul of quite a few aspects. I've already overhauled quite a bit of it, however, for the sake of uploading I've hardcoded some bits in. I WILL sort this, just not right now!!

I've created a bunch of extra sound fx, gun sounds mainly. Also the pain noise needs to be different for each player, Binky has a very deep voice for a lady!

Anyway... I've been staring at this screen all day... time to spend some time with my girlfriend!!

Very nice work...

I have a suggestion for making aiming easier. I tried to aim in a certain direction, and then fire the gun (without walking). For left/right, up/down, this is not a problem, but it is very hard to do this for diagonal directions. For instance if you press up+right, in most cases one of the two buttons will be read separately when you release the keys.

I noticed this problem in one of my own games as well, and solved it by building in a small delay when selecting a new direction: if the player "activates" a different direction than the current one, the character won't immediately change into that direction. First he will have to keep selecting that direction (= keep the keys down) for a number of game loops. (If all keys are released during this count, the counter would be reset to zero.)

This way if he is pressing up+right and tries to release them both at the same time, non-perfect timing is allowed for. Of course the time delay (the target count) should be very small so that movement (when you actually *want* to change the direction, as is most often the case) is not slowed down noticably.

On a related note, 360 degree rotational movement might also work well with this game.

The change in the player graphics makes such a difference, looks so much better now.

I found a bug though. When you go down to the bottom of the test map there are a number of the spidery face hugger type things that go for you. It's actually possible to get completely stuck if they surround you, where you can't fire and can't move in any direction.

Rob, see my editor changes above, which you've missed fromthe latest update.

Any plans for Doors that can be re-closed?

Would be great for a self destruct level.
"Contamination in Med Lab 1. Initiating Lock down and Self Destruct in 3 minutes"

I found a bug though. When you go down to the bottom of the test map there are a number of the spidery face hugger type things that go for you. It's actually possible to get completely stuck if they surround you, where you can't fire and can't move in any direction.
If you can't fire you're out of ammo, if you're surrounded by aliens with no ammo you're dead... It just so happens that unlimited health is switched on. Switch unlimited ammo on (or switch unlimited health off) and try and re-create that 'bug'.

I have a suggestion for making aiming easier. I tried to aim in a certain direction, and then fire the gun (without walking). For left/right, up/down, this is not a problem, but it is very hard to do this for diagonal directions. For instance if you press up+right, in most cases one of the two buttons will be read separately when you release the keys.
Not a bad idea... Might look into that it sounds pretty straight forward to implement. I did consider the turn left/right forwards backwards, but I think it works well as directional control.

Mark T, Sorry... Doing it now. I really wasn't concentrating on the editor at all today.

Mark 1, Doors reclosing sounds like a good idea... currently the way the doors open is by effectively destroying map information so making them close again would be a touch tricky... I like the idea of a bunch of doors closing behind you (or infront of you if you're not fast enough). I'll have a think about how to do it without having to hard code anything.

Code.zip updated with editor changes.

Oh, sorry you don't like the new hud, guys. Bit miffed about that, but hey ho, you don't like it, you don't like it. Fair enough. Here's the old weapons.bb code to revert to the previous hud followed by the hud.bb and old (changed) hud.png to put in gfx\hud

Global bulletgfx = LoadAnimImage("gfx\weapons\bullets.png",8,8,0,4)
MaskImage bulletgfx,255,0,255


Type weapon
	Field id
	Field name$
	Field maxClip,damage#
	Field ammo_in_a_clip
	Field bulletSpeed#
	Field fireRate ; in ms (passed to player when they get given the weapon)
	Field fireSoundChannel,fireSound
	Field outOfAmmoSoundChannel,outOfSound
	Field reloadSoundChannel,reloadSound
	Field muzzleFlashImage
	Field btype
	Field hud_image
End Type



Function setupWeapons(modifier#=1.0)
	; create the weapons we want in the game
	;createWeapon.weapon...
	
	; default gun [weaponBroadhurst] pistol
	; 	rating = crappy
	; 	speed = instant
	; 	damage = bad, so bad
	;	number of bullets = 1
	;	max clips = 5
	; 	bullets in a clip = 10
	
	; default gun [weaponDalton] machine gun
	; 	rating = kinda crappy
	; 	speed = medium
	; 	damage = very bad
	;	number of bullets = 1
	;	max clips = 5
	; 	bullets in a clip = 30

	; default gun [weaponRobinson] shotgun
	; 	rating = alright
	; 	speed = slow
	; 	damage = medium
	;	number of bullets = 4
	;	max clips = 5
	; 	bullets in a clip = 4

	; default gun [weaponRyxx] flamethrower
	; 	rating = pretty cool
	; 	speed = very slow
	; 	damage = medium/high
	;	number of bullets = N/A
	;	max clips = 1
	; 	bullets in a clip = 100


	; default gun [weaponStyrling] plasma light
	; 	rating = dudish, slow though
	; 	speed = very fast
	; 	damage = high
	;	number of bullets = 1
	;	max clips = 3
	; 	bullets in a clip = 5

	; default gun [weaponImpact] laser
	; 	rating = er... dunno really
	; 	speed = very fast
	; 	damage = high
	;	number of bullets = 1
	;	max clips = 3
	; 	bullets in a clip = 15
		
	;		...(id,name$,				dmg#,			maxclp,aiac,rate,	speed#,	bullettype,	image$,s1$="",s2$="",s3$="")
	createWeapon(1,weaponBroadhurst,    1.0*modifier, 	5, 	10, 	200, 	10.0, 	2,			21, "sfx\mgun.mp3", "", "sfx\pistolreload1.mp3")
	createWeapon(2,weaponDalton,    	0.7*modifier,	5, 	30,  	80, 	8.0, 	1,			22, "", "", "")
	createWeapon(3,weaponRobinson,  	0.5*modifier, 	10,	4, 		290,	10.0, 	0,			23, "", "", "")
	createWeapon(4,weaponRyxx,      	0.4*modifier, 	1, 100, 	140, 	3.0, 	-1,			24, "", "", "")
	createWeapon(5,weaponStyrling,  	7.0*modifier, 	3, 	5, 		1000,	15.0, 	3,			25, "", "", "")
	createWeapon(6,weaponImpact,    	6.5*modifier, 	3, 	15, 	220, 	3.0, 	3,			26, "", "", "")
End Function



; create a weapon instance. We only need one. It's more of a prototype.
Function createWeapon.weapon(id,name$,damage#,maxc,aiac,rate,speed#,bullettype,imageframe,s1$="",s2$="",s3$="")
	w.weapon=New weapon
	w\id=id
	w\name=name
	w\damage=damage
	w\maxClip=maxc
	w\hud_image=imageframe
	w\ammo_in_a_clip=aiac
	w\fireRate=rate
	w\bulletSpeed=speed
	w\btype = bullettype
	If s1<>""
		w\fireSound=LoadSound(s1)
		; USE: w\fireSoundChannel=PlaySound(w\fireSound)
	EndIf
	If s2<>""
		w\outOfSound=LoadSound(s2)
	EndIf
	If s3<>""
		w\reloadSound=LoadSound(s3)
		SoundVolume w\reloadsound,10.0
	EndIf
	Return w
End Function		

Function reloadPlayer(p.player)
	If p = Null Then Return
	If p\weaponClip[p\weapon\id]=0 Then Return	
	p\weaponClip[p\weapon\id]=p\weaponClip[p\weapon\id]-1
	p\weaponAmmo[p\weapon\id]=p\weapon\ammo_in_a_clip
	; player reload sound
	p\weapon\reloadSoundChannel=PlaySound(p\weapon\reloadSound)
End Function

Function givePlayerAmmo(p.player,amount=15)
	If p = Null Then Return
	If p\weapon=Null Then Return
	
	If p\weaponAmmo[p\weapon\id]+amount >= p\weapon\ammo_in_a_clip
		p\weaponClip[p\weapon\id] = p\weaponClip[p\weapon\id]+1
		; if we've picked up a clip and we have no ammo, then load it automatically.
		If p\weaponClip[p\weapon\id]=1 And p\weaponAmmo[p\weapon\id]=0 Then 
			reloadPlayer(p)
		EndIf
		; we can't have more clips than the weapon can allow.
		If p\weapon\maxClip <>-1
			; if the maxClip is -1 then you can hold an infinite number of clips
			If p\weaponClip[p\weapon\id] > p\weapon\maxClip Then p\weaponClip[p\weapon\id]=p\weapon\maxClip 
		EndIf
    Else
		p\weaponAmmo[p\weapon\id] = p\weaponAmmo[p\weapon\id] + amount
    EndIf	
End Function

Function getWeaponFromName.weapon(name$)
	name=Lower(name)
	For w.weapon=Each weapon
		If Lower(w\name)=name Then Return w
	Next
End Function	

Function giveWeaponToPlayer(p.player,w.weapon)
	If p = Null Then Return
	If w = Null Then Return
	p\weapon=w
	p\fireRate=p\weapon\fireRate
	p\tempFR = MilliSecs()
	If p\ownedWeapons[w\id]<>Null
		; already got it. Add one ammo.		
	Else
		; give this weapon to the player
		p\ownedWeapons[w\id]=w
		p\weaponAmmo[w\id]=w\ammo_in_a_clip
	EndIf
End Function

Function EquipWeapon(id,w.weapon)
	If w = Null Then Return

	If id=1 Then p.player=First player Else p.player=Last player

	ok=False
	For i=1 To 6
		If p\ownedWeapons[i]=w Then ok=True 
	Next

	If ok=False Then Return
	
	; make a sound
	If p\weapon<>w ; so we only play the sound once. We're using keydown
		p\weapon=w
		p\weapon\reloadSoundChannel=PlaySound(p\weapon\reloadSound)	
		p\fireRate=p\weapon\fireRate
		p\tempFR = MilliSecs()			
	Else
		p\weapon=w
		p\fireRate=p\weapon\fireRate
		p\tempFR = MilliSecs()		
	EndIf
End Function


Function deleteWeapons()
	For i.weapon=Each weapon
		If i\fireSound<>0 Then FreeSound(i\fireSound)
		If i\outOfSound<>0 Then FreeSound(i\outOfSound)
		Delete i
	Next
End Function

; the force is here because if you use
; keydown() when shooting it will wait for the
; fireRate to time out. But if you use Keyhit()
; you'll want to override the fireRate timeout.
Function fireWeapon(p.player,angle,force=0)
	If p = Null Then Return
	If p\weapon = Null Then Return	

	; fire rate check
	If ((MilliSecs()-p\tempFR) > p\fireRate) Or force
		p\tempFR=MilliSecs()
		
		If p\weaponAmmo[p\weapon\id] > 0
				
			shoot(p,angle)
		
			If p\weapon\name<>weaponRyxx
				shakeScreen(1,1,100)
			EndIf
		EndIf
		
	EndIf
End Function





; --------------------------------




Type bullet
	Field x#,y#
	Field vx#,vy#	
	Field owner.player
	Field life,damage#
	Field btype,special
	Field scenary ;scenary bullets can hurt both bad guys and players
End Type

Function shoot(p.player,angle)
	Local offset#=26.0
	
	speed#=p\weapon\bulletSpeed
		
	;Check player can fire
	If p\Clip > 0 Or p\ammo > 0 Then
		
		Select p\weapon\id
			Case 6: ; laser
				b.bullet =New bullet
				b\owner = p				
				b\btype = p\weapon\btype
				b\damage=p\weapon\damage	
				b\life=-99 ; live until you hit a wall
				ang#=angle			
				b\x=p\x+Sin(ang)*offset
				b\y=p\y-Cos(ang)*offset					
				b\vx=Sin(ang)*speed
				b\vy=-Cos(ang)*speed
				b\special=True ; laser!			
		
			Case 5: ; something?
				b.bullet =New bullet
				b\owner = p				
				b\btype = p\weapon\btype
				b\damage=p\weapon\damage	
				ang#=angle		
				b\life=-99 ; live until you hit a wall	
				b\x=p\x+Sin(ang)*offset
				b\y=p\y-Cos(ang)*offset					
				b\vx=Sin(ang)*speed
				b\vy=-Cos(ang)*speed
				flames#=3.0
				createparticle(b\x,b\y,Sin(ang-45)*flames,-Cos(ang-45)*flames,20,0,6,large_blast_image,16)
				createparticle(b\x,b\y,Sin(ang+45)*flames,-Cos(ang+45)*flames,20,0,6,large_blast_image,16)				
				createparticle(b\x,b\y,Sin(ang+90)*flames/2,-Cos(ang+90)*flames/2,20,0,6,small_blast_image,16)
				createparticle(b\x,b\y,Sin(ang-90)*flames/2,-Cos(ang-90)*flames/2,20,0,6,large_blast_image,16)
		
		
			Case 3: ; shotgun?
				For i=1 To 3
					b.bullet =New bullet
					b\owner = p		
					b\life=-99 ; live until you hit a wall			
					b\btype = p\weapon\btype
					b\damage=p\weapon\damage	
					ang#=angle+Rnd(-3,3)
					speed=speed+Rnd(-0.1,0.1)		
					b\x=p\x+Sin(ang)*offset
					b\y=p\y-Cos(ang)*offset					
					b\vx=Sin(ang)*speed
					b\vy=-Cos(ang)*speed
				Next
								
			
			Case 2: ; plasma gun
				b.bullet =New bullet
				b\owner = p
				b\life=50
				b\btype = p\weapon\btype
				b\damage=p\weapon\damage	
				ang#=angle			
				b\x=p\x+Sin(ang)*offset
				b\y=p\y-Cos(ang)*offset					
				b\vx=Sin(ang)*speed
				b\vy=-Cos(ang)*speed
				flames#=3.0
				createparticle(b\x,b\y,Sin(ang-45)*flames,-Cos(ang-45)*flames,20,0,6,large_blast_image,16)
				createparticle(b\x,b\y,Sin(ang+45)*flames,-Cos(ang+45)*flames,20,0,6,large_blast_image,16)	
								
			Case 4: ;flamethrower				
				For i=1 To Rand(4,6)
					b.bullet =New bullet
					b\owner = p
					b\life=50
					b\btype = p\weapon\btype
					b\damage=p\weapon\damage
					ang#=angle+Rnd(-3,3)
					
					b\x=p\x+Sin(ang)*offset
					b\y=p\y-Cos(ang)*offset		
					
					b\vx=Sin(ang)*speed
					b\vy=-Cos(ang)*speed	
					
					ex#=Rnd(-0.4,0.4)
					ey#=Rnd(-0.4,0.4)	
					If Rand(1,2)=1 				
						createparticle(b\x,b\y,(b\vx*4)+ex,(b\vy*4)+ey,80,0,6,large_blast_image,16)						
					Else
						createparticle(b\x,b\y,(b\vx*3)+ex*2,(b\vy*3)+ey*2,40,0,9,small_blast_image,16)
					EndIf
				Next
						
				
			Default:
				b.bullet =New bullet
				b\owner = p
				b\life=-99 ; live until you hit a wall
				b\btype = p\weapon\btype
				b\damage=p\weapon\damage
				ang#=angle
				b\x=p\x+Sin(ang)*offset
				b\y=p\y-Cos(ang)*offset		
				
				b\vx=Sin(ang)*speed
				b\vy=-Cos(ang)*speed
				
				; create bullet shell flying out.
				out#=Rnd(2,5)
				ang=ang+Rand(-7,3)
				createparticle(p\x+Sin(ang)*(offset*0.6),p\y-Cos(ang)*(offset*0.6),Sin(ang+90)*out,-Cos(ang+90)*out,Rand(20,50),3,3,bulletgfx,1)
		End Select		
		
		;Update counter for player status
		p\shots = p\shots + 1
		; create muzzle flash
		If use_sound
			p\weapon\fireSoundChannel=PlaySound(p\weapon\fireSound)		
		EndIf
		;Update clip and ammo count
		If Not unlimited_ammo Then p\weaponAmmo[p\weapon\id]= p\weaponAmmo[p\weapon\id] - 1
		If p\weaponAmmo[p\weapon\id] = 0 Then
			reloadPlayer(p) ; reload the player's weapon.
		EndIf
	Else
		;TODO: no fire - empty click sound
		p\weapon\outOfAmmoSoundChannel=PlaySound(p\weapon\outOfSound)
	EndIf
	
End Function

Function foebullet(x#,y#,angle,btype)
	b.bullet =New bullet
	b\scenary = 1
	b\life=100
	b\btype = btype
	b\damage=5	
	b\x=x
	b\y=y
	speed=3
	b\vx=Sin(angle)*speed
	b\vy=-Cos(angle)*speed
End Function	

Function explode(x#,y#)

ang=Rand(0,359)

For n=1 To 20
	b.bullet =New bullet
	b\scenary = 1
	b\life=20
	b\btype = 2
	b\damage=5
		
	b\x=x
	b\y=y
	
	speed#=Rnd(1,3)		
	
	b\vx=Sin(ang)*speed
	b\vy=-Cos(ang)*speed	
	
	ex#=Sin(a)
	ey#=Cos(a)
	If Rand(1,2)=1 				
		createparticle(b\x,b\y,(b\vx)*3,(b\vy)*speed,80,0,6,large_blast_image,16)						
	Else
		createparticle(b\x,b\y,(b\vx)*3,(b\vy)*speed,40,0,9,small_blast_image,16)
	EndIf
	ang=ang+Rand(20,80)
Next
				
End Function

Function handleBullets()
	For b.bullet=Each bullet
		; each bullet has a damage from its parent weapon;
		; use b\damage when hitting aliens and stuff.
		
		ok=True		
		
		bx=b\x 
		by=b\y 
		
		If bx < 32 Then ok=False
		If by < 32 Then ok=False
		If bx > 7552 Then ok=False
		If by > 7552 Then ok=False
		If b\life<0 Then ok=False
		If b\life=-99 Then ok=True
		
		If ok=True			
			result=gettile(b\x,b\y,LayerCollision)
			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

			For al.alien = Each alien
				If ImagesCollide(bulletgfx,b\x,b\y,1,		aliengfx(al\image),al\x-al\offset,al\y-al\offset,al\dir)
				;circlesoverlap(al\x,al\y,al\radius,bx+16,by+16,3)
					
					killed = alien_hit(al,b\damage)
					If killed = True And b\scenary<>1 Then
						;Update counter for player status
						b\owner\kills = b\owner\ammo + 1

					End If
					ok=False ;bullet hit alien
					
					
				EndIf
			Next
			
			; scenary bullets (explosions, cannons, aliens shooting) can hurt the player
			If b\scenary = 1
				For pl.player = Each player
					If circlesoverlap(b\x-4,b\y-4,4,pl\x-32,pl\y-32,playerradius)
					
					Player_affectHealth(pl,b\damage)
						
					ok=False ;bullet hit player
								
					EndIf
				Next
			EndIf
		EndIf
		
		If b\life<>-99 Then b\life=b\life-1
		b\x=(b\x+b\vx)
		b\y=(b\y+b\vy)		

		If ok=False Then kill_bullet(b)		
	Next	
End Function

Function kill_bullet(b.bullet)

	; different blast based on the bullet type	
	If b\btype=0 Then blastHere(Rand(1,2),b\x-16,b\y-16,0.3)
	If b\btype=1 Then blastHere(Rand(1,2),b\x-16,b\y-16,0.3)
	If b\btype=2 Then blastHere(Rand(1,2),b\x-16,b\y-16,0.3)
	If b\btype=3 Then blastHere(Rand(1,2),b\x-16,b\y-16,0.3)
	
	Delete b.bullet
		
End Function

Function drawBullets()
	For b.bullet=Each bullet
		bx=b\x-GOffsetX
		by=b\y-GOffsetY
		If b\special=0
			If b\btype<>-1 Then DrawImage bulletgfx,bx-3,by-3,b\btype
		Else
			; draw a red laser line
			i.bullet=Before b
			If i<>Null
				bx2=i\x-GOffsetX
				by2=i\y-GOffsetY
				; only draw on screen
				If bx2 < GraphicsWidth() And bx2 > 0
					If by2 < GraphicsHeight() And by2 > 0
						Color 255-Rand(5,20),0,0
						Line bx2,by2,bx,by
					EndIf
				EndIf
			EndIf
		EndIf
	Next	
End Function

as well as the old hud.bb
; simple hud
Global hud_block,hud_background,lives_block


Function setupHUD()
	hud_background=LoadImage("gfx\hud\hud.png")
	; create block images on the fly
	hud_block=CreateImage(4,16)
	SetBuffer ImageBuffer(hud_block)
	Color 96,24,0 : Rect 0,0,4,2 : Rect 0,14,4,2
	Color 144,40,0: Rect 0,2,4,2 : Rect 0,12,4,2
	Color 180,84,0: Rect 0,4,4,2 : Rect 0,10,4,2
	Color 216,140,0:Rect 0,6,4,4
	SetBuffer BackBuffer()	

	; this'll have a green hue
	lives_block=CreateImage(4,16)
	SetBuffer ImageBuffer(lives_block)
	Color 0,96,24 : Rect 0,0,4,2 : Rect 0,14,4,2
	Color 0,144,40: Rect 0,2,4,2 : Rect 0,12,4,2
	Color 0,180,84: Rect 0,4,4,2 : Rect 0,10,4,2
	Color 0,216,140:Rect 0,6,4,4
	SetBuffer BackBuffer()	
End Function

Function drawHUD()
	vy=GraphicsHeight()-40

	; no transparency
	DrawBlock hud_background,0,vy
	
	; draw player lives, ammo and health
	For p.player=Each player
	
		; draw a black background.
		Color 150,150,150
		Rect 0,(GraphicsHeight()-ImageHeight(hud_background))-33,GraphicsWidth(),33,1
		Color 0,0,0
		Rect 0,(GraphicsHeight()-ImageHeight(hud_background))-33,GraphicsWidth(),1,1 
	
		y=(GraphicsHeight()-ImageHeight(hud_background))-32	
		If p=First player
			gx=2
		Else
			gx=GraphicsWidth()-34
		EndIf
	
		For u=0 To 5
			If p\ownedWeapons[u+1]<>Null				
				If p=First player
					; check to see whether this is the selected weapon
					If p\ownedWeapons[u+1]=p\weapon
						Color 180,180,180
						Rect gx+(u*33),y,32,32,1
					EndIf
					DrawImage objects,gx+(u*33),y,p\ownedWeapons[u+1]\hud_image
				Else 
					; check to see whether this is the selected weapon
					If p\ownedWeapons[u+1]=p\weapon
						Color 180,180,180
						Rect gx-(u*33),y,32,32,1
					EndIf
					DrawImage objects,gx-(u*33),y,p\ownedWeapons[u+1]\hud_image				
				EndIf
			EndIf
		Next	
	
		; health
		If p=First player Then x=62 Else x=382
		y=2
		For i=1 To p\health Step 3
			DrawBlock hud_block,x+((i/3)*4),vy+y
		Next		

		; clips
		If p=First player Then x=62 Else x=382
		y=24
		For i=1 To p\Clip
			DrawBlock hud_block,(x+(i*5)),vy+y
		Next
		If p\Clip > 5		
			Color 255,255,255
			; indicate we've got more than 5 clips with a plus '+'
			If p = First player Then Text 50,vy+16,"+" Else Text 211,vy+16,"+"
		EndIf

		; ammo
		If p=First player Then x=114 Else x=432
		y=24
		
		ammo=p\ammo
		clipSize=p\weapon\ammo_in_a_clip
		width#=(65/6)
		k#=(width/clipSize)*ammo
		
		For i=1 To k
			DrawBlock hud_block,x+(i*6),vy+y
		Next

		; keys
		y=18
		j1=0 : j2=0 ; count variables
		
		For pu.pickedup = Each pickedup			
			ok=True
			Select Objectdef(pu\obj)\image
				Case 2,3,4,5:		frame=Objectdef(pu\obj)\image
				Default:			ok=False
			End Select
			; if ok=false then we're attempting to draw 
			; a pickedup object that isn't a key.
			; we only want to draw objects that are given
			; the ok by the above select.
			If ok
				If pu\player=1
					j1=j1+1
					Viewport 250,vy+y,55,20
					DrawImage Objects,(240+(10*j1)),vy+y,frame
					Viewport 0,0,GraphicsWidth(),GraphicsHeight()
				EndIf
				If pu\player=2
					j2=j2+1
					Viewport 570,vy+y,55,20
					DrawImage Objects,560+(10*j2),vy+y,frame
					Viewport 0,0,GraphicsWidth(),GraphicsHeight()
				EndIf
			EndIf					
		Next
		
		; draw credits
		If p=First player Then x=254 Else x=575
		y=6
		btext x,vy+y,p\credits
	Next
	
End Function

And the old Hud image with the lives bit taken out.
www.3030deathwar.co.uk/downloads/ab/hud.png

Oh, you can also delete the relevant images:

gfx\hud\heart.png
gfx\hud\playeronehealth.png
gfx\hud\playertwohealth.png
gfx\weapons\bulletframe.png
gfx\weapons\bullet.png
gfx\weapons\gas.png

as they're no longer needed and they're just making the gfx file bloated.

Rims, in the words of Kate Bush in a very high pitched voice,

"Don't give up, you still have us"

I'm not going to revert it back, it's a cake load better than the original, I think it just needs some tweaking!

Here's my little suggestion... Although can't be arsed to code it right now, so over to you...

The bullet frame is a bit poo, and as people have said it's a bit low-tech for alien breed.

The corner bits are a really cool shape though and I thought if you put them in the opposite corner (top-left to bottom-right) you have a good shape in the corner of the screen to display your health in a sort of curvey good to bad effect (if you get what I mean)

Then put a bit between this bits to make the rest of the hud. I don't think keys need displaying, but I think the guns and ammo should be displayed there, everything else is going to be in the inventory so we don't need to see it all the time.

Sketch:

No idea what the bit in the middle would be for by the way... just though it might be handy for shared info?! Oh and the white bits at the top would be transparant so it looks cool!

I'm still with you baby, I just hate drawing. Your hud: I like, I like. I'm on it, but I may be slow. Just started a new job and it's tiring, so damn tiring.

has anyone got any spare time to do specific graphics? We could use some nice gun images for both hud and floor.

I'll get on this though. As in tomorrow after 6. bloody work.

Rims, I can sort the gun images. I'll just do my rendering 3D images thing with models from polycount.

sounds good. Perhaps you could render the gun spinning a-la quake. That'd be nice.

Just a thought... I was looking through the screenshots up there ^^^ and I think we need to have all the wall tiles on the block overlay layer rather than the base layer. This would mean that particles will not sit on the top of the walls. Also it means if anything cuts over the wall at all it'll go under it rather than over it which should minimise the effect. I'll write a patchette for the editor so sort this, I think it will improve the look of the game.

Rob,

Along these lines, I was thinking that we should split the tile image graphics. We need to split base tiles from overlaid detail tiles. So whilst you're in there doing some stuff, maybe you could look at this too?

Having said that, I started rejigging all of the editor.bb code last night to tidy things up, as it is in a bit of a state. That's a fairly major task, so if you do make changes, keep 'em simple, and post them here - I'll update the editor over the next couple of days.

Oh, and can someone add me to the credits on the next update? ;-)

Mark, I'm not going to touch the editor for a while.

I agree to split the tile set. What I was thinking is that there will be the base tiles, and the transparant tiles. The block over tiles will be the base pack set, just displayed after the transparant tiles.

The block over tiles will basically be the same as the base coat just drawn in a different place (after all the sprites). We could probably be more effecient and make it so the base layer has an order value so but I don't think it's worth the bother quite frankly.

Of course this does mean when I split the tile set into 2 the test map will go to crap and will have to be re-done from scratch!

OK, Just written this little proggy to split the tiles into 2 files... It worked first time would you believe it!

Anyway... Got to create transparant versions of a few more tiles before these files will be 100%, but it works really well this!! Sorry... just really impressed with myself!

img=LoadImage("gfx/tiles.png")
maxtile=(ImageWidth(img)/32)*ImageHeight(img)/32
FreeImage img
tiles = LoadAnimImage("gfx/tiles.png",32,32,0,maxtile)

Global gotr=0
Global gotg=0
Global gotb=0

Function GetRGB(buffer,x,y)
	argb=ReadPixelFast(x,y,buffer)
	gotr=(ARGB Shr 16) And $ff 
	gotg=(ARGB Shr 8) And $ff 
	gotb=ARGB And $ff
End Function

SetBuffer BackBuffer()

Dim tile(maxtile-1)
For n=0 To maxtile-1
	mag=0
	blk=0
	DrawBlock tiles,0,0,n
	LockBuffer BackBuffer()
	For x=0 To 31
		For y=0 To 31
		GetRGB(BackBuffer(),x,y)
		If gotr=255 And gotg=0 And gotb=255 Then tile(n)=1:mag=mag+1
		If gotr=0 And gotg=0 And gotb=0 Then blk=blk+1
		Next
	Next
	
	If mag=1024 Then tile(n)=-1
	If blk=1024 Then tile(n)=-1

	UnlockBuffer BackBuffer()
	Cls:Text 0,50,"Processing tile:"+n:Flip
Next

i1=0
i2=0
For n=0 To maxtile-1
	If tile(n)=0 Then i1=i1+1
	If tile(n)=1 Then i2=i2+1
Next

i1=(i1/15)*32
i2=(i2/15)*32

image1 = CreateImage(480,i1+32)
image2 = CreateImage(480,i2+32)

x1=0:y1=0
x2=0:y2=0

Cls:Text 0,50,"Creating Image files":Flip

For n=0 To maxtile-1
	If tile(n)=0
		SetBuffer(ImageBuffer(image1))
		DrawBlock tiles,x1*32,y1*32,n
		x1=x1+1
		If x1=15 Then y1=y1+1:x1=0
	EndIf
	
	If tile(n)=1
		SetBuffer(ImageBuffer(image2))
		DrawBlock tiles,x2*32,y2*32,n
		x2=x2+1
		If x2=15 Then y2=y2+1:x2=0
	EndIf
Next

SetBuffer BackBuffer()

Cls:Text 0,50,"Saving Image files":Flip

SaveImage (image1,"image1.bmp")
SaveImage (image2,"image2.bmp")


heh, nice little program there rob, you resourceful monkey.

Just got to write the map remapper now so the map doesn't got to pants.

Mark, let me know when you're ready with the editor updates. Make sure you include the 2 tile sets, one for the solid tiles and one for the transparant tiles.

Just throwing in some more encouragement, you are all doing a grand job, its looking and playing very well, and I have to say it, I dont actually mind the flying bullets in the hud ;).

I had a bit of a play of and noticed that in a 2 player game, the players sometimes get stuck when 1 is walking near a door or walls and the other player gets too close.

Also, are you meant to be able to pick up more than 2 guns in the version so far?

Great work never the less, and I had lots of fun blasting through the test level.

Great work! Keep it going.

I had a bit of a play of and noticed that in a 2 player game, the players sometimes get stuck when 1 is walking near a door or walls and the other player gets too close.
Yeah, the collision is a bit iffy... looking at it. I thought I'd written in if the collision circles were overlapping but you were heading in the other direction you should move. That's not working... Need to fix that bit.
Also, are you meant to be able to pick up more than 2 guns in the version so far?
You can, it's just that the players are not set up to at the moment, you can re-jig the player.txt file to change their properties.

Another thought that I'll look into...

The aliens class should hold 2 more variables attack and defence. These will be how much an attack hurts you and how much an attack hurts them. This would mean instead of having massive health scores for the bad guys they would just be tougher. Also it means that the big beasties will hurt you more than the face huggers.

I'll add the two sets of tiles to the editor in amongst my other tweaks. Hopefully have the updated version tomorrow night.

I'll make sure the new tiles sets are uploaded this evening then ready.

Mark, I've uploaded the new tile sets and a new map that I think has sort-of remapped correctly(ish)... probably not as I completely destoryed my version of alien breed on my machine to try and get it to work! I didn't spend too much time trying to get it to work as I know you're going to be revamping all the draw map bits to make this work with the different tilesets.

Righty-ho the download

Once again... another thought... Weapons this time... Instead of hard coding what weapons each player can hold why not give each weapon a weight and have a maximum weight that each player can hold, so you could have several light weapons or one heavy for example.

Hi rob,

I remember the original of this! it all looks pretty faithful from what i can remember.

One thing though, the big alien at the start gets a bit stuck in small spaces, and some of the objects hard to pick up when between barrels.

Dan

Zips updated... Split the base and detail tiles apart, updated the editor and rest of the game to deal with this. Updated the map so it works with the updated tile sets.

Added a bunch of sound fx in the game

Added more wavs for the aliens although this is not implemented yet, but they are in the alien folders for future use.

Something I forgot to mention is that I've slightly buggered the editor up a bit (rooms creation doesn't work) in order to hack this 2 tileset stuff in quickly, but fear not, Mark said he's going to be posting up a new editor shortly.

Yup, editor tidy up is well under way, and I'll post whatever I've got tonight, although it may not be 100% complete. Rob, could you post what you've changed in the editor - I'm sure it's fairly trivial, but just to be sure...and make my life a bit easier! ;-)

In Map.BB It now has dmaxtile and bmaxtile and dtiles and btiles for detail and base.
; recently moved from editor include.bb
Global dmaxtile 				= 0 ;set in loadGraphics below
Global dmaxobject 			= 0
Global bmaxtile 				= 0 ;set in loadGraphics below
Global bmaxobject 			= 0
Global dtiles,btiles,objects,cursor,tiles_small
Global maxobject

Function loadGraphics$()
	; load tiles
	img=LoadImage("gfx/base.png")
	bmaxtile=(ImageWidth(img)/32)*ImageHeight(img)/32
	FreeImage img
	btiles = LoadAnimImage("gfx/base.png",32,32,0,bmaxtile)
	
	img=LoadImage("gfx/detail.png")
	dmaxtile=(ImageWidth(img)/32)*ImageHeight(img)/32
	FreeImage img
	dtiles = LoadAnimImage("gfx/detail.png",32,32,0,dmaxtile)
	MaskImage dtiles,255,0,255	
	
	; load objects
	img=LoadImage("gfx/objects.png")
	maxobject=(ImageWidth(img)/32)*ImageHeight(img)/32
	FreeImage img
	objects = LoadAnimImage("gfx/objects.png",32,32,0,maxobject)
	MaskImage objects,255,0,255
	
	; load small tiles
	;tiles_small = LoadAnimImage("gfx/tiles_small.png",16,16,0,maxtile)
	;MaskImage tiles_small,255,0,255
	
	cursor = LoadImage("gfx/cursor.png")
	MaskImage cursor,255,0,255	
	
End Function


Palette drawing bit in editor.bb not takes into account btile and dtile and swaps the palette set as necessary.
		If activelayer = layerbase Or activelayer=LayerBlock And tileno<bmaxtile Then DrawBlock btiles,((n-(Floor(n/23)*23))*32)+32,490+(Floor(n/23)*32),n+tileoffset
		If activelayer = layerobject And tileno<maxobject Then DrawBlock objects,((n-(Floor(n/23)*23))*32)+32,490+(Floor(n/23)*32),n+tileoffset
		If activelayer = LayerDetail Or activelayer = LayerTop And tileno<dmaxtile Then DrawBlock dtiles,((n-(Floor(n/23)*23))*32)+32,490+(Floor(n/23)*32),n+tileoffset


Likewise the drawmap function (in map.bb) for the main game uses btiles and dtiles instead of tiles images.

Okay, here's a link to where you get the current editor. *clicky*

This turned out to be a lot harder than I anticipated, and also I ended up wanting to tidy the UI as much as the code.

This is a start. I don't *think* anything's broken; I've tested stuff I've moved around as I go, and it seems to hang together. Hope you like what I've done (and like the 'placeholders' for other stuff.

Couple of things that might catch you out when using it. Escape no longer exits - use the exit & confirm buttons. Also on the room editor, designing a room now works just like designing a map - click the tile in the palette, then apply to the room - it used to be the other way around. This does mean you have to switch between drawing rooms and drawing tiles, but this doesn't seem a big disadvantage (and simplified a whole chunk of code too!). Also I've moved the x/y w/h stuff to the bottom left, and the activetile id to the right to standardise stuff.

Comments (and bugs!) pur-lease! I do intend to do more over the weekend, mainly the code tidy up as that's only halfway done.

Just before I go to bed... You can't mouse wheel scroll on the palette

Rob: I remeber you posting a comment about my MD3 thingy, I just wanted to let you know that I have a newer version up at http://blitz.drago-tech.net/files/Md3Version2Test.zip
the model you posted in the thread works, as well as all other model that I have tested, includes a 50meg model of a dragon rider.

Sorry for interupting this thread though. you can all throw stones and boo at me now ;)

laters Drago

ta rob, fixed that one - just move mz=MouseZSpeed() into handlePalette.

Will post an update with more sometime over the weekend.

I'm currently working on a major overhaul of the weapons and sound... coming along quite nicely but it's recoding massive chunks!

Hmm this project is coming along nicely. I might give a try to make some music for it, if anyone would let me know what formats and file sizes are acceptable. I'd like mp3, not midi and stuff. I could do a guitar solo with some midi rhythm maybe... My email is in my profile.

Zips updated.

Basically so much has been re-arranged you may as well delete what you've got and download the lot... well except the music folder that's not changed.

23-10-04 :
Rob:
- New SFX, all SFX now sits in the sfx folder
- Soundcontrol.bb takes over from sound.bb, all sounds are now loaded via the loadsfx function, this keeps track of all sounds loaded and makes sure the same sound isn't loaded twice... and stuff
- Weapons overhaul - I still think it needs further overhaul, however, created 4 new weapons to replace the existing ones. The weapons appear to be hard coded so most of the weapon creation stuff is pretty pointless. If we can soft code this we should, however, with special types I think it's going to be damn near impossible.
- Various other bits and pieces fixed, updated, enhanced and other words like that.

22-10-04 :
Mark:
- Editor Updated

20-10-04 :
Rob:
- Split base tiles from detail tiles, updated editor and various parts of the main game.
- Started to include sound fx into the game more
- Added sound fx into the alien folders, although no implemented yet


Oh, and can somebody please write the level completion goals stuff, you know:

Mandatory: Collect object x, go to location x,y
Optional : Activate object z

Type thing!

I'm going to make fire button 2 as activate object, this will be the button for getting into the intex systems and stuff, it'll do the same check to see what's around you.

I'll do the objectives - seeing as I'm in the depths of the editor already! I'll finish my tidy up first, then get onto the structure thing.

I'm thinking that a level will have 1-N mandatory objectives, and 0-N optional objectives. I also like the idea of gaining objectives as you work through a level, either mandatory or optional. (For example start a level thinking you need to retrieve something, but when you get there, discover it was a trap, so the mandatory objective is now escape).

I may also have a go at game structure too - i.e. how levels tie together. I think this ties up with objectives, as it's likely to be the case that completing an objective (mandatory OR optional) results in opening levels.

What I'd like though, are some ideas on how to present available levels to the user. I like the idea of a world map (defined as an image for the game) and each available starting level is a location on that map. You can then select any one of those locations to play.

That all sounds reasonable to me.

One more thing, whilst you're playing with the editor, we need an extra thingy...

I've just created a switch object, that when you switch it on it destorys itself and creates a detail layer item as a switched-on switch, this works fine, however there's no way of making this a custom object, ie, you need to switch on switches 4,6, and 15 for example, you've no way of knowing. Currenly the only way to do it would be to create copies of the same graphic, which is a poo.

What I think we need is a custom script for a particular tile x,y. This way we can make a switch open a door at x,y. Or a switch will complete an objective. Or opening a door will complete an objective. Or blowing up a barrel spawns an alien. You see what I mean? All the objects currenly are identical and therefore we have no control over a specific barrel or switch or door or whatever.

I can see 2 ways of doing this:
1: add an extra layer to the map that is the custom script number, 0 being no custom script, and have this link to another table of the custom scripts.

2: Create a new array of strings that is the map size and we just throw the custom script into each element (probably simplist).

What do you think?

I think I'd go down the route of more complicated scripts attached to the objects. If you want a switch that you can turn on and then turn off, the on switch would have script to replace the object with the of switch and vice versa. Something like: "remove(me)|createobjecthere(12)" 12 being the id of the off switch.

As for the same 'class' of object resulting in different actions, I did see this as a case of creating an object for each and every interaction, although remember that two objects can point at the same image. The reasoning here is that a) you need to write the script anyway (in your example against the tile), and why not keep all scripts in objects? I did add names to objects to try to help this duplication of objects, and I also reasoned that the number of objects that would need to trigger a separate item would be low enough for this to be acceptable.

If we've got a common set of items that we'd like to reuse many times, maybe we need to do as we have with keys and doors?

Not entirely sure on this, I'm still inclined to think keeping it in the object is the way to go, will sleep on it...

And now for tonights update. I'm happy now that the editor is tidy enough, both in code and GUI. Note that I've substantially changed collisions / hitpoint set up, hopefully to be a lot easier when building levels for real. More tooltips added too, and now they only appear if you hover for >1/2 second.

So if anyone else does want to do some work on the editor (tile pixel editing tool anyone?), please feel free. I do intend to work on levels settings and objectives next, but that will be fairly self contained now!

editor.bb
; blitz3d version

; keys

; arrow keys move around map
; pgup/pgdown scroll through tiles
; return - pick up tile
; numpad4 - place a ground of 4 squares starting with the selected tile ie 12
;                                                                          34
; Space - Promote background tile to a over block tile

Include "inc\scancodes.bb"
Include "inc\sounds.bb"
Include "inc\consts.bb"
Include "inc\gui.bb"
Include "inc\map.bb"
Include "inc\editor include.bb"
Include "inc\util.bb"
Include "inc\editor-room.bb"
Include "inc\editor-undo.bb"

; set up graphics
Graphics 800,600,32,0
SetBuffer BackBuffer()
loadGraphics() ; both version use this.
;had trouble when moving these to gui (symbFont did not exist for some reason)
Global font = LoadFont("Arial.ttf",15)
Global symbFont = LoadFont("Symbol.ttf",15)
InitGUI()

; check to load a level from the command line
If Len(CommandLine())
	load(CommandLine())	
Else
	createRandomMap()
EndIf

Global LastInput = MilliSecs()
Global sc_x=0
Global sc_y=0
Global scrolldelay = 0
Global mousescroll=False
Const setscrolldelay = 2
Global activelayer = 1
Global activetile = 0
Global tileoffset = 0
Global xpos
Global ypos
Global current_tool=1 ; 1=tile tool, 2=room tool
Global coll_tool=1

visible(LayerCollision)=0
visible(LayerHits)=0
refreshRoomList()
quickLoadRoom("maps\rooms\default.room")
statusMsg("Ready...")

main()
End

;---------------------------------------------

Function main()
	Repeat
	
		; very first thing we do is a clear screen!
		Cls
		
		; now work out where the mouse is : over the map or tileset / palette
		MouseOverMap	 = False
		MouseOverTileset = False
		If (MouseX()<640 And MouseY()<480) Then 
			MouseOverMap=True
		ElseIf (MouseY()>490 And MouseY()<586) And (MouseX()>32 And MouseX()<768) Then
			MouseOverTileset = True 
		End If
	
		; deal with the common GUI elements (the RHS of the screen)
		handleGUICommon()		
		
		; next deal with the main editing area of the screen
		Select current_tool
			Case 3
				Color 255,255,255
				Text 50,50,"An integrated tile editor here would be neat..."
				
			Case 6
				Color 255,255,255
				Text 50,50,"Define objectives here, with ID numbers"			
				Text 50,65,"Need to define what constitutes completion for each"				
				Text 50,80,"Each objective can have a script assigned to run on completion, which might use new commands like:"				
				Text 50,95,"startlevel(basement), givelevel(nebraska), giveitem(medal),giveobjective(12,mandatory)"				
				Text 50,110,"Assign at least one objective as mandatory to each player"
				Text 50,125,"Additional optional objectives can also be assigned to each"	
				Text 50,140,"Note that not all objectives need to be assigned at the start"							
				Text 50,155,"some can be gained by completing other objectives, some by object scripts"				
				
			Case 7
				Color 255,255,255
				Text 50,50,"Define level settings here, such as:"			
				Text 50,65,"tilesets to use, object file to use"				
				Text 50,80,"info to show on level select screen (image, name, text, map location xy)"				
				Text 50,95,"background music to play"				
				Text 50,110,"movie / image / text to show on starting level"
				
			Default
				handleMap(MouseOverMap)	
		End Select
			
		; ******************************** TOOL SPECIFIC GUI
			
		If current_tool=1 Then ; TILE TOOL
		
			; map stuff
			If MouseOverMap Then
				; tile highlighter
				Color 0,255,0
				Rect (xpos-Floor(sc_x/32))*32,(ypos-Floor(sc_y/32))*32,32,32,False	
										
				Select activeLayer				
					Case LayerCollision,LayerHits
						handleMapCollision()	
					Case LayerBase,LayerDetail,LayerBlock,LayerObject,LayerTop
						handleMapTile()		
				End Select
			EndIf			
			
			; tile tool GUI
			handlePalette(activelayer,MouseOverTileset) 
			
		ElseIf current_tool=3 Then ; TILE EDITOR
					
			; TODO: Tile Editor!
					
		ElseIf current_tool=2 Then ; ROOM TOOL
			handleRoom()	
			handlePalette(LayerBase,MouseOverTileset) 				
				
		ElseIf current_tool=4 Then ; FILE MENU
			handleFileMenu()
			
		ElseIf current_tool=5 Then ; EXIT MENU
			If Button(650,240,140,"Confirm?",False,False,KEY_NONE,"Click to exit the editor") Then Exit
			
		ElseIf current_tool=6 Then ; Objectives editor
		
			; TODO: Level objectives editor
		
		ElseIf current_tool=7 Then ; Level Settings editor			

			; TODO: Level settings editor
			
		EndIf     
		
		;******************** SCREEN UPDATE
		drawStatusBar()
		DrawToolTip()
		DrawImage cursor,MouseX()-1,MouseY()-1
		Flip
	
	Forever
End Function

Function handleGUICommon()
	;*************************** core GUI elements
		
	; layers / visibility
	For n=0 To NumLayers
		If current_tool=1 And activelayer=n Then act=True Else act=False
		If Button(650,n*20,100,layername(n),act,False,n+2,"Edit this map layer") Then
			activelayer=n				
			If activeLayer = LayerObject Then tileoffset = 0
			If activelayer = LayerHits Then coll_tool=4
			If activelayer = LayerCollision Then coll_tool=1
			current_tool=1
		EndIf
		
		If Button(760,n*20,30,"Vis",visible(n),False,KEY_NONE,"Show / Hide this layer on the map")
			visible(n)=(visible(n)+1) Mod 2
			Repeat:Until MouseDown(1)=False
		EndIf
	Next		
		
	; other drawing tools
	If Button(650,140,140,"Room Tool",current_tool=2,False,KEY_NONE,"Create and draw rooms") Then current_tool=2	
	If Button(650,160,140,"Tile Editor",current_tool=3,False,KEY_NONE,"Edit tiles") Then current_tool=3	
	If Button(650,180,140,"Objectives",current_tool=6,False,KEY_NONE,"Define level objectives") Then current_tool=6
	If Button(650,200,140,"Level Settings",current_tool=7,False,KEY_NONE,"Define level settings") Then current_tool=7	

	If Button(650,220,68,"File Menu",current_tool=4,False,KEY_NONE,"Load, save or create a new level") Then current_tool=4
	If Button(723,220,68,"Exit",current_tool=5,False,KEY_NONE,"Exit the editor") Then current_tool=5		
	
	ry=GraphicsHeight()-140
	If First undoTile<>Null ;Undo tools
		If Button(650,ry,68,"Undo",False,False,KEY_NONE,"Undo all changes up to last reset")
			doUndo():Delay 200
		ElseIf Button(723,ry,68,"Reset",False,False,KEY_NONE,"Reset undo history")
			clearUndo():Delay 200
		EndIf	
	EndIf
End Function

Function handleMap(mouseover)
	;********************* MAP
	; draw the selected layers of the map
	For n=1 To NumLayers
		If visible(n)=1 Then drawmap_inEditor(sc_x,sc_y,n)
	Next
	If visible(0)=1 Then drawcollision(sc_x,sc_y)
			
	; find mouse location on map
	If mouseover Then
		xpos = Floor(MouseX()/32)+Floor(sc_x/32)
		ypos = Floor(MouseY()/32)+Floor(sc_y/32)
		Button(2,510,28,xpos)
		Button(2,530,28,ypos)			
	Else
		xpos = -1
		ypos = -1
	EndIf

	;******************* INPUT HANDLING

	; map movement code (arrow keys and mouse near edge)
	key_left=False:key_right=False:key_up=False:key_down=False	
	If mouseover Then 
		If MouseX()<5 Then key_left=True
		If MouseX()>635 Then key_right=True
		If MouseY()<5 Then key_up=True
		If MouseY()>475 Then key_down=True		
		If key_left Or key_right Or key_up Or key_down Then
			If Not mousescroll Then mousescroll=True:scrolldelay=setscrolldelay
		Else
			mousescroll=False
		End If
	EndIf
	If KeyDown(KEY_ARROW_RIGHT) Then key_right=True
	If KeyDown(KEY_ARROW_LEFT) Then key_left=True
	If KeyDown(KEY_ARROW_UP) Then key_up=True
	If KeyDown(KEY_ARROW_DOWN) Then key_down=True
	
	; slow down the map scrolling a bit
	If scrolldelay < 0
		If key_right And sc_x<7552 Then sc_x=sc_x+32 : scrolldelay = setscrolldelay
		If key_left And sc_x>0 Then sc_x=sc_x-32 : scrolldelay = setscrolldelay
		If key_up And sc_y>0 Then sc_y=sc_y-32 : scrolldelay = setscrolldelay
		If key_down And sc_y<7552 Then sc_y=sc_y+32 : scrolldelay = setscrolldelay
	Else
		scrolldelay = scrolldelay - 1
	EndIf
End Function

Function handleMapCollision()

	If MouseDown(1) Then 
		Select coll_tool
			Case 1; static
				undoTile(xpos,ypos,map(xpos,ypos,LayerHits),map(xpos,ypos,LayerCollision))
				;if left control is held
				If KeyDown(KEY_CTRL_LEFT) Then	
					map(xpos,ypos,LayerCollision)=czVisStatic 
				Else 
					map(xpos,ypos,LayerCollision)=czStatic			
				EndIf
				map(xpos,ypos,LayerHits)=-1
				
			Case 2; dynamic
				undoTile(xpos,ypos,map(xpos,ypos,LayerHits),map(xpos,ypos,LayerCollision))
				;if left control is held
				If KeyDown(KEY_CTRL_LEFT) Then	
					map(xpos,ypos,LayerCollision)=czVisDynamic 
				Else 
					map(xpos,ypos,LayerCollision)=czDynamic			
				EndIf
				map(xpos,ypos,LayerHits)= lhDefaultHits ;see consts.bb for lhDefaultHits	
				
			Case 3; vis blocker
				undoTile(xpos,ypos,map(xpos,ypos,LayerHits),map(xpos,ypos,LayerCollision))				
				If map(xpos,ypos,LayerCollision)=czDynamic Then	
					map(xpos,ypos,LayerCollision)=czVisDynamic 
				ElseIf map(xpos,ypos,LayerCollision)=czStatic 
					map(xpos,ypos,LayerCollision)=czVisStatic			
				EndIf				
				
			Case 4; hitpoint tool
				undoTile(xpos,ypos,map(xpos,ypos,LayerHits),map(xpos,ypos,LayerCollision))	
				If map(xpos,ypos,LayerCollision)=czDynamic Or map(xpos,ypos,LayerCollision)=czVisDynamic Then
					;if left control is held
					If KeyDown(KEY_CTRL_LEFT) Then IncVal = 5 Else IncVal = 1 
					map(xpos,ypos,LayerHits) = map(xpos,ypos,LayerHits) + IncVal
					;limit the maximum value for a dynamic collisions hit points to 99
					If map(xpos,ypos,LayerHits) > lhMaxHits Then map(xpos,ypos,LayerHits) = 99
					Delay 100 ; not too quick now!	
				EndIf			
		End Select
			
	ElseIf MouseDown(2) Then 
		undoTile(xpos,ypos,map(xpos,ypos,LayerHits),map(xpos,ypos,LayerCollision))
		Select coll_tool
			Case 1,2
				map(xpos,ypos,LayerCollision)=0
				map(xpos,ypos,LayerHits) = 0
			Case 3
				If map(xpos,ypos,LayerCollision)=czVisDynamic Then	
					map(xpos,ypos,LayerCollision)=czDynamic 
				ElseIf map(xpos,ypos,LayerCollision)=czVisStatic 
					map(xpos,ypos,LayerCollision)=czStatic			
				EndIf
			Case 4; hitpoint tool
				undoTile(xpos,ypos,map(xpos,ypos,LayerHits),map(xpos,ypos,LayerCollision))	
				If map(xpos,ypos,LayerCollision)=czDynamic Or map(xpos,ypos,LayerCollision)=czVisDynamic Then
					;if left control is held
					If KeyDown(KEY_CTRL_LEFT) Then IncVal = -5 Else IncVal = -1 
					map(xpos,ypos,LayerHits) = map(xpos,ypos,LayerHits) + IncVal
					;limit the minimum value for a dynamic collisions hit points to 1
					If map(xpos,ypos,LayerHits) < 1 Then map(xpos,ypos,LayerHits) = 1
					Delay 100 ; not too quick now!
				EndIf						
		End Select				
	EndIf	
End Function

Function handleMapTile()
	; Left mouse button handler
	If MouseDown(1) Then
		If activelayer=LayerObject Then
			If activetile<numObjects					
				map(xpos,ypos,activelayer)=activetile+1
				If activetile = 37 ; give a barrel 5 hit points
					map(xpos,ypos,LayerHits) = 5
					map(xpos,ypos,LayerCollision)=czDynamic
				EndIf
			EndIf
		Else
			undoTile(xpos,ypos,map(xpos,ypos,activeLayer),map(xpos,ypos,LayerCollision))
			map(xpos,ypos,activelayer)=activetile+1					
		End If
	End If		
	
	;Right Mouse Button Layer Handler
	If MouseDown(2) Then
		Select ActiveLayer
		
			Case LayerBase
				undoTile(xpos,ypos,map(xpos,ypos,ActiveLayer),map(xpos,ypos,LayerCollision))
				map(xpos,ypos,activelayer) = 0
										
			Case LayerObject,LayerBlock,LayerDetail
				undoTile(xpos,ypos,map(xpos,ypos,ActiveLayer),map(xpos,ypos,LayerCollision))
				map(xpos,ypos,ActiveLayer) = 0									

		End Select
	EndIf		

; NB: removed promotion of base tile to top tile (SPACE) as they now have different tilesets!

	If KeyDown(KEY_ENTER) Then activetile = map(xpos,ypos,activelayer)-1 ; pick up tile (RETURN)

	If KeyDown(KEY_NUMPAD_4) ; place 4 concurrent tiles in a square (NUMPAD4!)
		; TODO: Undo in here
		map(xpos,ypos,activelayer)=activetile+1
		map(xpos+1,ypos,activelayer)=activetile+2
		map(xpos,ypos+1,activelayer)=activetile+3
		map(xpos+1,ypos+1,activelayer)=activetile+4
	EndIf		

End Function

Function handlePalette(layer,mouseover)
	
	mz = MouseZSpeed()
	
	;select the tileset to show in the palette	
	Select layer
		Case LayerBase, LayerBlock
			tileset=btiles : maxtile=bmaxtile
		Case LayerObject	
			tileset=objects : maxtile=maxobject
		Case LayerDetail,LayerTop
			tileset=dtiles : maxtile=dmaxtile
		Case LayerCollision,LayerHits
			If Button(40,510,100,"Static",coll_tool=1,False,KEY_S,"Set tiles to be indestructible walls, vis blocking with Left Control") Then 	coll_tool=1
			If Button(150,510,100,"Dynamic",coll_tool=2,False,KEY_D,"Set tiles to be destructible walls, vis blocking with Left Control") Then 	coll_tool=2
			If Button(260,510,100,"Vis-Blocker",coll_tool=3,False,KEY_V,"Set tiles to be barriers to vis checking") Then 	coll_tool=3	
			If Button(370,510,100,"Hit Points",coll_tool=4,False,KEY_H,"Set tiles to be barriers to vis checking, faster with Left Control") Then 	coll_tool=4								
			Return 
		Default
			Return ; no point hanging around - there's no relevant palette!
	End Select
	
	; active tile	
	Button(770,510,28,activetile,False,False,KEY_NONE,"The ID of the currently selected tile")			
	
	;draw the palette itself
	For n=0 To 68
		tileno = n + tileoffset
		If tileno<maxtile Then DrawBlock tileset,((n-(Floor(n/23)*23))*32)+32,490+(Floor(n/23)*32),n+tileoffset
		If tileno = activetile Then Color 0,255,0:Rect ((n-(Floor(n/23)*23))*32)+32,490+(Floor(n/23)*32),32,32,False
	Next

	; move around the palette
	tup=0

	;I know it looks like there is nothing in the quotes in the line below, but trust me, there is
	If (Button (0,490,32,"­",False,True,KEY_PAGE_UP,"Scroll up the tileset",False)=True Or (mz = 1 And MouseOverTileset)) And tileoffset > 0 And scrolldelay<0 Then tup=-1
	If (Button (768,490,32,"¯",False,True,KEY_PAGE_DOWN,"Scroll down the tileset",False)=True Or (mz = -1 And MouseOverTileset)) And scrolldelay<0 Then tup=1
	If KeyDown(KEY_NUMPAD_0) And scrolldelay<0 Then activetile = activetile +1:scrolldelay=10
	If KeyDown(KEY_CTRL_RIGHT) And scrolldelay<0 Then activetile = activetile -1:scrolldelay=10
	If tup<>0 Then tileoffset=tileoffset + (tup*23):scrolldelay=15		

	; handle mouse being over the palette
	If mouseover Then
		If MouseDown(1) Then 
			checktile = (Floor((MouseX()-32)/32))+(Floor((MouseY()-490)/32)*23)+tileoffset
			If (checktile < maxtile) And (checktile >-1) Then activetile = checktile
			;If currentRoom<>Null And scrolldelay<0 Then UpdateRoomBlock()	<-removed in changing how room tiles are selected
		ElseIf MouseDown(2)
			If Layer=layerObject And (First objdef<>Null)
				checktile = (Floor((MouseX()-32)/32))+(Floor((MouseY()-490)/32)*23)+tileoffset
				If (checktile < maxtile) And (checktile >-1) Then activetile2 = checktile
				If (activetile2+1) =< numObjects
					For n=1 To CountString(objectdef(activetile2+1)\trigger,"|")
						trigger$=parse(objectdef(activetile2+1)\trigger,"|",n)
						newdef$ = InputBox("New Script for trigger: '"+trigger+"'. ESC or RETURN to end",20,20,500,300,True,objectdef(activetile2+1)\script[n])
						If Len(newDef) > 0 Then objectdef(activetile2+1)\script[n] = newdef$
					Next														
				EndIf
			EndIf 		
		Else
			; move your mouse over it
			If Layer=layerObject And (First objdef<>Null)
				checktile = (Floor((MouseX()-32)/32))+(Floor((MouseY()-490)/32)*23)+tileoffset
				If (checktile < maxtile) And (checktile >-1) Then activetile2 = checktile				
				If (activetile2+1) =< numObjects
					If objectdef(activetile2+1)<>Null
						w=400
						count=CountString(objectdef(activetile2+1)\trigger,"|")
						h=80+(20*(count+1))
						Color 60,60,60:Rect 12,13,w,h,1:Rect 13,13+h,StringWidth("MB2 to edit")+5,FontHeight(),1
						Color 255,255,255 : Rect 10,10,w,h,1 : Rect 10,10+h,StringWidth("MB2 to edit")+5,FontHeight(),1
						Color 0,0,0 : Rect 10,10,w,h,0 : Rect 10,10+h,StringWidth("MB2 to edit")+5,FontHeight(),0
											
						Text 20,20,"Object: "+objectdef(activetile2+1)\name
						Text 20,40,"Image: "+objectdef(activetile2+1)\image
						Text 20,60,"Trigger: "+objectdef(activetile2+1)\trigger
						For n=1 To CountString(objectdef(activetile2+1)\trigger,"|")
							Text 20,80+(n*20),objectdef(activetile2+1)\script[n] 
						Next
						
						Text 12,10+h,"MB2 to edit"
					EndIf
				EndIf
			EndIf
		EndIf	
	EndIf
End Function

Function handleRoom()	
		
		;draw mini room
		rx=GraphicsWidth()-150
		ry=GraphicsHeight()-240
		drawRoom(rx,ry)
						
		; new room
		If Button(GraphicsWidth()-50,ry,48,"New",False)
			createBlankRoom():Delay 200 ; stop fast re-clicking	
				
		; load room					
		ElseIf Button(GraphicsWidth()-50,ry+20,48,"Load",False)
			; load the listbox containing possible rooms.
			num=ListDlg(rx-200,ry-230,180,330,room_list)
			If num<>-1
				which$=SL_Strings(room_list,num)			
				If Instr(which,".room")<>0
					quickLoadRoom(which)
				EndIf
			EndIf
			Delay 200 	
			
		; save room
		ElseIf Button(GraphicsWidth()-50,ry+40,48,"Save",False)
			saveRoom():Delay 200		
		EndIf

		; draw room
		If xpos>-1 Then
			If MouseDown(1)
				If create_a_room_sx=-1 And create_a_room_sy=-1
					;DebugLog "setting up room..."
					create_a_room_sx=xpos
					create_a_room_sy=ypos
				EndIf
			Else
				If create_a_room_sx<>-1 And create_a_room_sy<>-1
					;DebugLog "Creating room..."
					; ready to finish.					
					createRoomHere(create_a_room_sx,create_a_room_sy,xpos,ypos)	
					create_a_room_sx=-1
					create_a_room_sy=-1										
				EndIf
			EndIf	
		Else
			create_a_room_sx=-1		
			create_a_room_sy=-1		
		End If
		
		; draw other GUI elements
		If create_a_room_sx=-1 And create_a_room_sy=-1
			statusMsg("Use LMB on map to start top left corner of a room, and drag and release to size",0)
			; tile highlighter
			Color 0,255,0
			x=(xpos-Floor(sc_x/32))*32:y=(ypos-Floor(sc_y/32))*32
			Line x,y,x+32,y
			Line x,y,x,y+32			
		Else
			Button(2,510,28,create_a_room_sx)
			Button(2,530,28,create_a_room_sy)				
			Button(2,550,28,Int(Abs(create_a_room_sx-xpos)+1))
			Button(2,570,28,Int(Abs(create_a_room_sy-ypos)+1))
			; tile highlighter
			Color 0,255,0
			x=(xpos-Floor(sc_x/32))*32:y=(ypos-Floor(sc_y/32))*32
			Line x+32,y,x+32,y+32
			Line x,y+32,x+32,y+32
			Line create_a_room_sx*32-sc_x, create_a_room_sy*32-sc_y, 32+create_a_room_sx*32-sc_x, create_a_room_sy*32-sc_y
			Line create_a_room_sx*32-sc_x, create_a_room_sy*32-sc_y, create_a_room_sx*32-sc_x,    32+create_a_room_sy*32-sc_y					
		EndIf
		
End Function


Function handleFileMenu()
		; FILE TOOLS
		ry=240
		If Button(650,ry,140,"New",False,False,KEY_F8,"Create a new map") Then createRandomMap()
		
		If Button(650,ry+20,140,"Save",False,False,KEY_F1,"Save the current map") Then
			FileName$ = InputBox("Enter filename to save (without .map extension)",200,300,400,40,True,"maps")
			If Len(FileName) <1 Then Return
			If FileType(FileName) = 2 Then Return
			If Instr(filename,".map") Then Replace(filename,".map","")
			save(filename)
			;Save last file name
			fileout = WriteFile("prefs.txt")
				WriteString fileout,FileName$
			CloseFile(fileout)		
		End If
		
		If Button(650,ry+40,140,"Load",False,False,KEY_F4,"Load an existing map") Then 
			FileName$ = InputBox("Enter filename to load (without .map extension)",200,300,400,40,True,"maps")
			If Instr(filename,".map") Then Replace(filename,".map","") ; just in case
			load(filename) ; call common load.
			moveToStartPosition()		
			DebugLog "Loaded: " + FileName$
			statusMsg("Loaded: " + FileName$)
		End If
		If Button(650,ry+60,140,"Load Last",False,False,KEY_F5,"Load the last edited map") Then LoadLast():moveToStartPosition()
End Function

Function moveToStartPosition()

	; find a start position and move the screen there
	For i=0 To 254
		For j=0 To 254
			tile=map(i,j,LayerObject)
			If tile>0
				For k=0 To 5
					script$=Lower(objectdef(tile)\script[k])										
					If Instr(script,"positionplayer")						
						; we've found it...
						sc_x=((i-10)*32)
						sc_y=((j-10)*32)
						Return
					EndIf
				Next
			EndIf
		Next
	Next
End Function

and gui.bb
;Global font = LoadFont("Arial.ttf",15)
;Global symbFont = LoadFont("Symbol.ttf",15)

Include "inc\stringlist.bb"
Dim CharWidths(1)

Const KeyRepeat% = 100
Const CaratSymbol$ = "¦"

Type TRGB
	Field ColRed%
	Field ColGreen%
	Field ColBlue%
End Type


Global myRGB.TRGB = New TRGB

Type TCarat
	;X and Y in characters not pixels
	Field X%
	Field Y%
	Field PX%
	Field PY%
	Field Symbol$ = "¦"
End Type

Type TKeyState
	Field Scancode%
	Field Ascii%
	Field Shift
	Field Ctrl
End Type


Type TLine
	Field LineNum%
	Field Txt$
	Field Owner$
End Type


;;;;;;;;;;;;;;;;;;;;
; FUNCTION InitGUI ;
;;;;;;;;;;;;;;;;;;;;
Function InitGUI()

	SetFont font

;INPUT BOX SETUP
;Get the character widths
Dim charwidths(512)
	charwidths(32) = 4
	For c = 33 To 512
		charwidths(c) = StringWidth(Chr$(c))
	Next
End Function


;;;;;;;;;;;;;;;;;;;
; FUNCTION Button ;
;;;;;;;;;;;;;;;;;;;

Function Button(x,y,width,name$,active=False, symbol=False, ShortCutKey=KEY_NONE,tip$="", ShowShortCut = True, OffCol%=$A0A0A0,  OverCol%=$C8C8C8, TextCol%=$000000)
;VARS
	Local result = False
	
;MAIN

	If (ShortCutKey > KEY_NONE)  Then 
		If ShowShortCut Then name = name + " ("+ KeyNames(ShortCutKey) +")"
		tip$ = tip$ + " ("+ KeyNames(ShortCutKey) +")"		
	EndIf
		
	If RectsOverlap(MouseX(),MouseY(),1,1,x,y,width,16) Then 
		myRGB = GetRGB(OverCol)
		Color myRGB\ColRed, myRGB\ColGreen, myRGB\ColBlue
		tooltip(MouseX(),MouseY(),tip$)
	Else 
		myRGB = GetRGB(OffCol)
		Color myRGB\ColRed, myRGB\ColGreen, myRGB\ColBlue
	EndIf
	Rect x,y,width,16

	myRGB = GetRGB(TextCol)
	Color myRGB\ColRed, myRGB\ColGreen, myRGB\ColBlue


	If Symbol Then SetFont symbFont

	Text x+width/2,y+7,name,True,True

	If Symbol Then SetFont Font

	If active Then Color 255,0,0:Rect x,y,width,16,False
	If RectsOverlap(MouseX(),MouseY(),1,1,x,y,width,16) And MouseDown(1) Then result = True

	If ShortCutKey > KEY_NONE Then
		If KeyDown(ShortCutKey) Then Result = True
	EndIf

	Return Result
	
End Function 



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; FUNCTION InputBox									 ;
; Parameters:                                        ;
; msg$ : any message to display above the input box  ;
; x,y : top left coords                              ;
; width, height: outer width and height of inputbox  ;
; rtnSubmit : if the return key is pressed, then     ;
;             automatically submit the text			 ;
;             If this is false, then an OK button is ;
;             displayed at the bottom of the box     ;
; DefaultText : automatically inserts this into the  ;
; input box, the carat will be positioned at the end ;
; of this text.                                      ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Function InputBox$(Msg$,x,y,width,height,rtnSubmit=True, DefaultText$="", BGCol%=$FFFFFF, TextCol%=$000000, BorderCol%=$A0A0A0)

	SetFont Font

;;;;;;
;vars;
;;;;;;

	Local leave = False
	Local InnerWidth = Width - 6
	Local InnerHeight = Height - 40
	Local InnerX = 6
	Local InnerY = 20
	Local retString$ = ""
	Local fntWidth = FontWidth()/4
	Local fntHeight = FontHeight()
	Local EditWin = CreateImage(width, height)
	Local BGImage = CreateImage(GraphicsWidth(), GraphicsHeight())
	Local Delimiters$ = "|() "+Chr$(13)
	Local FoundDelim = False
	Local TopLine = 0
	
	Local Carat.TCarat = New TCarat
			Carat\X = 0
			Carat\Y = 0

	Local SelStart% = 0 
	Local SelEnd% = 0
	Local CaratPos% = 0
	
	Local ScrollPos = 0
	
	Local ScrollWidth = 12

;;;;;;
;main;
;;;;;;

CopyRect 0,0,GraphicsWidth()-1, GraphicsHeight()-1, 0,0, GraphicsBuffer(), ImageBuffer(BGImage)

If rtnSubmit Then InnerHeight = Height - 6
FlushKeys()
CaratPos = Len(DefaultText)
retString = retString+DefaultText

Repeat
	;quit the function without returning any text if escape is pressed
	If KeyHit(1) Then 
		leave = True
		retString$ = ""
	EndIf
	
	;;;;;;;;;;;;;;;;;;;;;
	;get character input;
	;;;;;;;;;;;;;;;;;;;;;
	get = GetKey()
	
	
	
	If Get = 13 Then
		If rtnSubmit Then 
			leave = True ;if return is pressed and rtnSubmit is True then leave
		Else
			LeftSide$ = Left$(retString$,CaratPos)
			RightSide$ = Right$(retString$, Len(RetString)-CaratPos)
			retString$ = LeftSide$ + Chr(get) + RightSide$
			CaratPos = CaratPos + 1
		EndIf
	ElseIf KeyDown(14); backspace
		If MilliSecs()-LastInput > KeyRepeat Then
			If Len(retString$) > 0 Then 
				LeftSide$ = Left$(retString$,CaratPos)
				RightSide$ = Right$(retString$, Len(RetString)-CaratPos)
				retString$ = Left$(LeftSide$, Len(LeftSide$) -1) + RightSide$
			EndIf
			LastInput = MilliSecs()
			CaratPos = CaratPos - 1
			If CaratPos < 0 Then CaratPos = 0
		EndIf
	ElseIf KeyDown(203) ;Left Cursor
		If MilliSecs()-LastInput > KeyRepeat Then
		
			If KeyDown(157) ; CTRL ; Allow CTRL+Left to jump the carat pos to the next delimiter to the left of the cursor
				For i = CaratPos To 1 Step -1
					
					d = 1
					While (d < Len(Delimiters$)+1) And (Not FoundDelim )	
						
						If Mid(retString$, i,1) = Mid(Delimiters$, d,1) Then 
							CaratPos = i-1
							FoundDelim = True
						EndIf
						d = d + 1
					
					Wend
				
					If FoundDelim Then Exit
				Next
				
				If Not FoundDelim Then CaratPos = CaratPos - 1
				If CaratPos < 0 Then CaratPos = 0
				
				FoundDelim = False
			
			Else
				CaratPos = CaratPos - 1
				If CaratPos < 0 Then CaratPos = 0
			EndIf
			LastInput = MilliSecs()
		EndIf
	ElseIf KeyDown(205) ; RIGHT CURSOR
		If MilliSecs()-LastInput > KeyRepeat Then
			If KeyDown(157) ;CTRL ; Allow CTRL+RIGHT to jump the carat pos to the next delimiter to the right of the cursor

				For i = CaratPos+1 To Len(retString$)
					d = 1					
					While (d < Len(Delimiters$)+1) And (Not FoundDelim )

						If Mid(retString$, i,1) = Mid(Delimiters$, d,1) Then 
							CaratPos = i
							FoundDelim = True
						EndIf
						d = d + 1
					Wend				

					If FoundDelim Then Exit
				Next
				
				If Not FoundDelim Then CaratPos = CaratPos + 1
				If CaratPos > Len(retString$) Then CaratPos = Len(retString$)
				
				FoundDelim = False
		
			Else
				CaratPos = CaratPos + 1
				If CaratPos > Len(retString$) Then CaratPos = Len(retString$)
			EndIf
			LastInput = MilliSecs()
		EndIf

	ElseIf KeyDown(207) ; end
		CaratPos = Len(retString$)
	ElseIf KeyDown(199) ; home
		CaratPos = 0
	ElseIf (get > 31) ;is a letter, number or symbol

		LeftSide$ = Left$(retString$,CaratPos)
		RightSide$ = Right$(retString$, Len(RetString)-CaratPos)
		retString$ = LeftSide$ + Chr(get) + RightSide$
		CaratPos = CaratPos + 1
		
	EndIf

		
		;Draw the edit window
		oldbuffer = GraphicsBuffer()
		SetBuffer ImageBuffer(EditWin)
			
			myRGB = GetRGB(BorderCol)
			Color myRGB\ColRed, myRGB\ColGreen, myRGB\ColBlue
			Rect 0,0,width,height

			myRGB = GetRGB(BGCol)
			Color myRGB\ColRed, myRGB\ColGreen, myRGB\ColBlue
			Rect 2, 17, InnerWidth , InnerHeight

			myRGB = GetRGB(TextCol)
			Color myRGB\ColRed, myRGB\ColGreen, myRGB\ColBlue
			
			Text width/2,8,msg,True,True
			
			Viewport 2, 17, InnerWidth ,InnerHeight
			
			;Draw Edit Content
			Local WCount% = 0
			Local LineCount% = 0
			Local CharCount% = 0
			Local tempString$ = ""
			;Local tempString2$ = ""
			Local StringLength% = Len(retString$)
			Local CurrentChar$ = ""
			Local LineLength% = 0

			While CharCount < StringLength

				Repeat
					CharCount = CharCount + 1
					Currentchar$ = Mid$(retString$, CharCount,1)	
					If Charcount = CaratPos+1 Then tempString$ = tempString$ + CaratSymbol$
					tempString$ = tempString$ + CurrentChar$
					charWidth = CharWidths(Asc(CurrentChar))
					WCount = WCount + charWidth
				Until (WCount > InnerWidth - 8) Or (CharCount = StringLength) Or (Asc(CurrentChar) = 13)

				If Right$(tempString,1) = Chr(13) Then 
						tempString$ = Left(TempString$,Len(TempString$)-1)
				EndIf

			
				myRGB = GetRGB(TextCol)
				Color myRGB\ColRed, myRGB\ColGreen, myRGB\ColBlue
				If (CaratPos = StringLength) And (CaratPos = CharCount) Then tempString = tempString + CaratSymbol
				Text InnerX,InnerY + (LineCount * fntHeight),tempString$,False,False
				
				LineCount = LineCount + 1
			
				tempString$  = ""

				WCount = 0


			Wend
		
		SetBuffer OldBuffer
	
	DrawBlock BGImage,0,0
	DrawBlock EditWin,x,y	
	
	Viewport 0,0,GraphicsWidth(), GraphicsHeight()
	
	If Not rtnSubmit Then
		If Button(x+(Width/2)-95,y+Height-20,80,"OK",False,False,KEY_INSERT,False,BorderCol,BGCol,TextCol) Then leave = True
		If Button(x+(Width/2)+5,y+Height-20,100,"Cancel",False,False,KEY_ESCAPE,False,BorderCol,BGCol,TextCol) Then
			retString = ""
			leave = True
		EndIf
	EndIf
	
	DrawImage cursor,MouseX()-1,MouseY()-1
	
	Flip

Until leave = True

	FlushKeys()
	FlushMouse()

	

	FreeImage EditWin
	FreeImage BGImage

	Return retString$

End Function


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; FUNCTION GetRGB                            ;
; converts an int to R/G/B colour            ;
; returns a type of TRGB                     ;
; example Usage:                             ;
; myRGB.TRGB = New TRGB                      ;
; myRGB = GetRGB($FF55FF)                    ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Function GetRGB.TRGB(InColour%)

Local tempRGB.TRGB = New TRGB
	tempRGB\ColRed = InColour% Shr 16 And 255 Shl 0
	tempRGB\ColGreen = InColour% Shr 8 And 255 Shl 0
	tempRGB\ColBlue = InColour% Shr 0 And 255 Shl 0
Return tempRGB

End Function


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; FUNCTION ListBox                       ;
; returns the index of the selected item ;
; parameters:                            ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Function ListDlg%(X%, Y%, Width%, Height%, Items.TStringList,  BGCol%=$FFFFFF, TextCol%=$000000, BorderCol%=$A0A0A0)
;			myRGB = GetRGB(TextCol)
;			Color myRGB\ColRed, myRGB\ColGreen, myRGB\ColBlue

SetFont Font

;;;;;;
;VARS;
;;;;;;

	Local BorderWidth% = 2
	
	Local InnerX% = X + BorderWidth
	Local InnerY% = Y + BorderWidth

	Local InnerWidth% = Width - (BorderWidth * 2)
	Local InnerHeight% = Height - (BorderWidth * 2)

	Local ListWin = CreateImage(Width, Height)
	Local BGImage = CreateImage(GraphicsWidth(), GraphicsHeight())

	Local Selected% = -1
	Local ScrollOffset% = 0
	Local ScrollBarWidth = 12
	Local leave = False
	Local Result = -1
	Local FirstMouseHit = 0
	Local SecondMouseHit = 0
;;;;;;
;MAIN;
;;;;;;

CopyRect 0,0,GraphicsWidth()-1, GraphicsHeight()-1, 0,0, GraphicsBuffer(), ImageBuffer(BGImage)

If SL_GetCount(items) < 1 Then Return -1

While Not Leave

	;check keyboard input
	If KeyHit(1) Then 
		Result = -1
		Leave = True
	EndIf
	
	;28 Or 156 = Return Or Enter
	If KeyHit(28) Or KeyHit(156) Then
		Result=Selected
		Leave = True
	EndIf
	
	;up arrow
	If KeyHit(200) Then 
		selected = selected -1
		If selected < 0 Then selected = 0
	EndIf
	
	;down arrow
	If KeyHit(208) Then 
		selected = selected + 1
		If selected > SL_GetCount(Items)-1 Then Selected = SL_Getcount(Items)-1
	EndIf
	

	;check mouse input
	
	mx = MouseX()
	my = MouseY()

	If (mx > InnerX+2) And (mx < InnerX + (InnerWidth-ScrollWidth-4)) Then
		If (my > InnerY+2) And (my < (InnerY + (InnerHeight-2)) ) Then
			If MouseDown(1) Then			

				Selected = ( ( (my - InnerY) - ScrollOffset) / FontHeight())
				If Selected < 0 Then selected = 0
				If selected > SL_GetCount(Items)-1 Then selected = SL_Getcount(Items)-1

			EndIf
			
			;maybe do a tooltip here if the item the mouse 
			;is over is longer than the width of the list

		EndIf
	EndIf


	;draw listbox

	If SL_GetCount(items) * FontHeight() > InnerHeight Then 
		ScrollBarWidth = 16
	Else 
		ScrollBarWidth = 0
	EndIf
	
	InnerWidth = ((Width - (BorderWidth * 2)) - ScrollBarWidth ) + 4

	oldbuffer = GraphicsBuffer()
	SetBuffer ImageBuffer(ListWin)

	;draw Border
	myRGB = GetRGB(BorderCol)
		Color myRGB\ColRed, myRGB\ColGreen, myRGB\ColBlue
		Rect 0, 0, Width, Height, True

	;Draw background
	myRGB = GetRGB(BGCol)
		Color myRGB\ColRed, myRGB\ColGreen, myRGB\ColBlue
		Rect BorderWidth, BorderWidth, InnerWidth - (BorderWidth*2), InnerHeight - (BorderWidth), True

	;clip to inner rectangle
	Viewport BorderWidth + 2, BorderWidth + 2, InnerWidth-8, InnerHeight-8
	
	;draw the text
	For i% = 0 To SL_GetCount(items)-1
		If i = selected Then
		
			;draw a rect over the selected area
			myRGB = GetRGB(TextCol)
			Color myRGB\ColRed, myRGB\ColGreen, myRGB\ColBlue
			;for some reason, changing the following colour to black (0,0,0), draws as transparent

			;Color 10,10,10
			Rect BorderWidth + 2, (i * FontHeight())+ ScrollOffset, InnerWidth%, FontHeight(), True

			myRGB = GetRGB(BGCol)
			Color myRGB\ColRed, myRGB\ColGreen, myRGB\ColBlue

		Else
			myRGB = GetRGB(TextCol)
			Color myRGB\ColRed, myRGB\ColGreen, myRGB\ColBlue
		EndIf

		Text BorderWidth+2, (i * FontHeight())+ ScrollOffset, SL_Strings(items, i), False, False
	Next
	
	Viewport 0,0,Width,Height
	
	
	SetBuffer oldbuffer
	
	DrawBlock BGImage,0,0
		
	DrawBlock Listwin, x,y


	mz = MouseZSpeed()
  
	If Button (X + Width - ScrollBarWidth, y + BorderWidth, 12, "­", False, True) Or mz = 1 Then
		;scroll up
		ScrollOffset = ScrollOffset + FontHeight()
		If ScrollOffset > 0 Then ScrollOffset = 0
	EndIf
	
	If Button (x + Width - ScrollBarWidth, (y + Height - FontHeight()) - BorderWidth, 12, "¯", False, True) Or mz = -1 Then
		;scroll down
		ScrollOffset = ScrollOffset - FontHeight()
		If ScrollOffset < -( (SL_GetCount(Items)-1) * FontHeight()) Then ScrollOffset = -( (SL_GetCount(Items)-1) * FontHeight())
	EndIf


	If selected > -1 Then s$ = SL_Strings(items, selected)
	If StringWidth(s$)>InnerWidth-4 Then 
		tooltip(InnerX+2,InnerY + (Selected * FontHeight())+ScrollOffset,s$)
	EndIf

	DrawToolTip()

	DrawImage cursor,MouseX()-1,MouseY()-1

	Flip

Wend

FreeImage ListWin
FreeImage BGImage

Return result

End Function

;;;;;;;;;;;;;;;;;;;;
; FUNCTION ToolTip ;
;;;;;;;;;;;;;;;;;;;;

Global tt_txt$,tt_currtxt$,tt_x,tt_y,tt_w,tt_h,tt_starttime%
Const tt_waittime%=500

Function ToolTip(x,y,txt$)
	tt_txt$=txt$
	tt_w=StringWidth(txt$)+4
	tt_h=FontHeight()+4
	If x+tt_w+4>GraphicsWidth() Then tt_x=GraphicsWidth()-tt_w Else tt_x=x
	If y+tt_h+4>GraphicsHeight() Then tt_y=GraphicsHeight()-tt_h Else tt_y=y
End Function

Function DrawToolTip()
	If tt_txt$<>"" Then 
		If tt_txt$ = tt_currtxt$ Then
			; only actually draw it if we've exceed the wait time
			If tt_starttime+tt_waittime<MilliSecs() Then
				Color 255,255,225
				Rect tt_x,tt_y,tt_w,tt_h,True
				Color 0,0,0
				Rect tt_x,tt_y,tt_w,tt_h,False
				Text tt_x+2,tt_y+2,tt_txt$
			End If
		Else
			; if it's a new tooltip, reset the timer
			tt_starttime=MilliSecs()
		End If
	End If
	tt_currtxt$=tt_txt$	
	tt_txt$=""
End Function

;;;;;;;;;;;;;;;;;;;
; Status Messages ;
;;;;;;;;;;;;;;;;;;;

Type statusMsg
	Field msg$,life
End Type

Function statusMsg(mr$,lifetime=100)
	If lifetime=0 Then Delete Each statusMsg
	m.statusMsg=New statusMsg
	m\msg=mr
	m\life=lifetime
End Function

Function drawStatusBar()
	o.statusMsg=First statusMsg
	Color 255,255,255	
	If o = Null
		txt$=txt+"Undo Level: "+countUndos()
	Else
		; draw the status message here...
		txt$=o\msg
		o\life=o\life-1
		If o\life < 0 Then Delete o
	EndIf
	Text 3,GraphicsHeight()-14,txt
End Function


I'm really sorry guys, I've been incredibly busy. I've added player inventories. Don't forget to include it as inventory.bb. It's a start... you can move around and select certain items.

Whenever you pick up an item, use: InvItem_Create(name$,weight,p.player,image,frame)
to add it to a player's inventory. You can specify an image and frame to be drawn.

Players have backpack sizes. Items have sizes as well.

; inventory
Const INVITEM_WIDTH = 70

Type invItem
	Field owner.player
	Field name$,weight
	Field x,y,image,imageFrame
End Type


Function useInventoryItem(p.player,i.invItem)
End Function


Function InvItem_Create(name$,weight,p.player,image,frame)
	i.invitem=New invItem
	i\name=name
	i\weight=weight	
	i\image=image
	i\imageFrame=frame
	addInvItem(p,i)		
End Function

Function setPlayerInvLevels(p.player,maxWeight)
	p\maxWeight=maxWeight
	p\weight=0
	p\itemsCarrying=0	
	p\showingInv=0
	p\overWait=MilliSecs()
End Function

Function showInventory(p.player)
	p\showingInv=1-p\showingInv
End Function

Function UpdateInventories(p.player)
	; this handles the controls
	If p=First player
		max=(GraphicsWidth()/2)-10
	Else
		max=(GraphicsWidth())
	EndIf
	
	If (MilliSecs()-p\overWait) > 200
		If control(pright,p\id)
			p\over=p\over+1
			p\overWait=MilliSecs()
			
		ElseIf control(pleft,p\id)
			p\over=p\over-1
			p\overWait=MilliSecs()
		EndIf	
	EndIf
	If p\over<0 Then p\over=p\itemsCarrying-1
	If p\over>p\itemsCarrying-1 Then p\over=0
End Function

Function addInvItem(p.player,item.invItem)
	If p=Null Then Return
	
	If item= Null Then
		DebugLog "Inv Item is NULL"
		Return		
	EndIf
	
	If p\weight+item\weight <= p\maxWeight
		; pickup
		item\owner=p
		p\inv[p\itemsCarrying]=item
		p\weight=p\weight+item\weight
		p\itemsCarrying=p\itemsCarrying+1
		refreshInvItems()	
	Else
		DebugLog "Cannot pick up "+item\name+". It's too heavy"
		Return
	EndIf	
End Function

Function refreshInvItems()

	For p.player=Each player
		If p = First player
			min=5
			x=min
			max=(GraphicsWidth()/2)-10
		Else
			min=(GraphicsWidth()/2)+10
			x=min
			max=(GraphicsWidth())
		EndIf
	
		y=10
		If p\itemsCarrying>0
			For i=0 To p\itemsCarrying-1
				inv.invItem=p\inv[i]
				If inv<>Null								
					If x > max-INVITEM_WIDTH
						x=min
						y=y+INVITEM_WIDTH
					EndIf
					inv\x=x
					inv\y=y	
					x=x+INVITEM_WIDTH
				EndIf
			Next
		EndIf
	Next
End Function

Function drawInvItems(p.player)
	If p\showingInv=0 Then Return
	
	If p=First player
		max=(GraphicsWidth()/2)-10
		; player one
		Color 150,150,150:Rect 0,0,max,GraphicsHeight()
		Color 200,200,200:Rect max,0,1,GraphicsHeight()
		btext 10,GraphicsHeight()-10,p\weight+" / "+p\maxWeight
	Else
		max=(GraphicsWidth()/2)-10
		Color 150,150,150:Rect max,0,GraphicsWidth()/2,GraphicsHeight()
		Color 200,200,200:Rect max,0,GraphicsWidth()/2,GraphicsHeight()
		btext (GraphicsWidth()/2)+10,GraphicsHeight()-10,p\weight+" / "+p\maxWeight
	EndIf

	If p\itemsCarrying>0
		For i=0 To p\itemsCarrying-1
			inv.invItem=p\inv[i]
			If inv<>Null
				If inv\image<>0
					DrawImage inv\image,inv\x,inv\y,inv\imageFrame
				EndIf
				Color 255,255,255
				Rect inv\x,inv\y,INVITEM_WIDTH,INVITEM_WIDTH,0
				Color 0,0,0
				bText inv\x+INVITEM_WIDTH/2,inv\y+INVITEM_WIDTH/2,inv\name,1,1
				If i=p\over
					Color 255,0,0
					Rect inv\x,inv\y,INVITEM_WIDTH,INVITEM_WIDTH,0
				EndIf
			EndIf
		Next
	EndIf	
End Function


And to see it in action I'm using the action button to open/close it.

Add this to the start of the player_create function as it'll set the player's backpack size and add some temporary objects. The size could be set using the readItem functions.
	setPlayerInvLevels(pl,500)
	For i=1 To 5
		InvItem_Create("A_"+i,"50",First player,0,0)		
	Next

Change this in player.bb->Player_UpdateAll() function.
	If control(paction,pl\id) Then showInventory(pl)

	If pl\showingInv=0

		; 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
			
		; weapons (player specific)
		If control(p_weapon1,pl\id) Then EquipWeapon(pl\id,getWeaponFromName("Machine Gun"))
		If control(p_weapon2,pl\id) Then EquipWeapon(pl\id,getWeaponFromName("Shot Gun"))
		If control(p_weapon3,pl\id) Then EquipWeapon(pl\id,getWeaponFromName("Flame Thrower"))
		If control(p_weapon4,pl\id) Then EquipWeapon(pl\id,getWeaponFromName("Grenade Launcher"))
		If control(p_weapon5,pl\id) Then EquipWeapon(pl\id,getWeaponFromName(weaponStyrling))
		If control(p_weapon6,pl\id) Then EquipWeapon(pl\id,getWeaponFromName(weaponImpact))
		
		If control(pAction,pl\id) Then runActionScript(pl) ; action key
					
		If control(pfire1,pl\id)
			fireWeapon(pl,pl\dir*22.5)
			firing = True		
		EndIf
	Else
		UpdateInventories(pl)
	EndIf


By the way, nice new weapon graphics, rob. very nice.

I'm happy with pointing scripts at different objects but how do you go about doing it? Does it mean you need to have a physical object in the anim strip like the player 1 and player 2 start, likewise with the respawns and alien positioning. If this is the case I'm not sure it's the right way to go as we're going to have more and more specials and therefore have a big anim strip for the sake of the editor. Having a seperate animstrip for the game is just going to get complicated.

I've probably got the wrong end of the stick so put me right!

Not very helpful code wise, but keep up the excellent work guys!

Oh one more thing... we could probably do with a new kind of collision too... This would say if the collision block goes all the way to the ceiling, this would mean the grenades wouldn't collide with them when it's at the top of it's flight, that would be cool because then you could throw grenades over the top of barrels and shelving.

Okay, let's list all the kinds of things we want to script, this'll help me to get my head round this.

generic collectibles (guns, ammo, health, keys)
generic doors
generic scenery (barrels, destructible rubble)
switch(es) to open / close a door
switch(es) to trigger self destruct

Rob, your comment on imagelists - yes, we'd end up with many specific items in the object list in the editor, but they can reuse images, so no overhead on gfx files. And as object definition files can be per level, you only see objects for that level. But yes, for objects not visible when playing you would need an object image for the editor. Although I'm half tempted to say the editor puts the object id on the map if there's no image assigned.

Having said that, I am beginning to think that all levels should have access to a default.obj (with generics) and a level.obj (with specifics).

I am also considering the idea of a single .obj file for all levels that defines classes of objects. We then allow these objects to accept parameters (e.g. x,y, timedelay). The current object map layer then becomes a string, not an integer that defines the object class and the input parameters. For example a switch object might be object id 56, and to get it to open a door at 10,11 on the map, the object layer is now "56 10 11". Which is almost your idea for scripts attached to tiles...

As you can see, I'm still thinking!

How about this:

We have a single object definition file which is used as the palette for the editor. This includes self-contained generic objects, where the ID is simply stamped on the map object layer, and picked up and run in game (as currently).

However, it also includes specially tagged 'template' objects. These appear in the editor palette for placement on the map, but when placed create a new object in a new level.obj file, with a new ID (from a fixed starting ID, e.g. 100). All properties are copied over from the template to the new object. These properties can then be edited by selecting the item on the map (MMB?). You can then make the sxcript do what you want. the assigning of the ID, creation of a copy in the level.obj, and stamping of the new ID on the map location are all handled by the editor.

For example, we define a template switch object. We could include script in the template to opendoor(x,y). On placing the switch on the map, you then open up that instance of the object script and change x,y to the correct door location.

The main game then loads the generic objects in all instances (although template object IDs never actually get used). If a level has a specific object file defined, that gets loaded too. The appropriate script can then be run for each object.

This sorts out switches opening doors, etc. What it doesn't really do is sort out interdependencies between objects. I'm thinking the most flexible approach here might be some script commands like setvar(<variablename>,<value>) and checkvar(<variable>,<value>).

So, if we have three switches, of which 1+3 must be on and 2 off to open a door, each switch includes "setvar(switchN,on)|checkvar(switch1,on)|checkvar(switch2,off)|checkvar(switch3,on)|opendoor(10,11)" (N=1-3)

However, I've just realised that to make a switch turn on and back off again, and so on, will need some slightly more complex scripting - i.e. if...then constructs. In a format like "If(variable,value,actiontrue,actionfalse)". So the template code for an on/off switch would need to look like:

"if(<switchname>,on,setvar(<switchname>,off)|setimage(23),setvar(<switchname>,on)|setimage(24))"

Where the user is expected to change <switchname> in the script when it is placed. (We could even be clever and prompt for these...). The bit of script to run on flicking the switch could then either exist in the 'true' part of the above template if it's just dependent on that switch, or after the If block if we want to check a whole bunch of other vars too. So, finally(!) our three switches look like:

if(switch1,on,
  setvar(switch1,off)|setimage(23),
  setvar(switch1,on)|setimage(24))|
checkvar(switch1,on)|
checkvar(switch2,off)|
checkvar(switch3,on)|
opendoor(10,11)


Does that make any sense? ;-)

>>all levels should have access to a default.obj (with generics) and a level.obj (with specifics).

I agree, everything in the current obj file is a generic, this was my point about having a seperate look up for the scripts on a per tile basis.

>>layer then becomes a string, not an integer that defines the object class and the input parameters

I think you're over complicating it, if you just had the string holding the overwriting script it would be a done job. And if you had the complete script in the string then you could do away with level.obj file.

Infact... (Thinking as I type) Once we have a compile level function in the editor we could make it so it puts the generic scripts onto the script layer and then the game doesn't need any obj files at all.

And what do you think about the idea of a "cannot reach the ceiling" flag on the collision layer?

Posting at the same time...

I think you're making it too complicated, yes it's really flexible but I don't think we need anything like as complex as that. The puzzles in a game like this are really only use item A with object B or switch on object C. What you're suggesting sounds really cool, but I don't think it'll be worth the effort.

Oh, another thing... I think the rooms should have 4 square in the middle then we can make larger floor tiles (2x2) things ans still use the room creator.

I've just updated the player movement a bit more to sort out a lot of gripes about the strafe fire, I've improved this loads, very subtle but much better. Created bigger explosions.

Also had a thought about the collision to ceiling thing that might just work! I'll get back to you on that one!

I've just created an exploding barrel animation, when you blow up barrels now they go flying into the air! Look really cool!!

And the collision to ceiling thing I've got round that by saying if the hit points on a grid position is >-1 and the grenade is less that 2/3rds through it's path then it won't collide. This means you can throw grenades over the top of barrels... It's very cool... The downside is that you can also throw grenandes through closed doors... Hmmm... maybe needs a bit of tweaking... That said, I don't think doors should be shootable anyway so they could all be set to -1.

OK, before anyone pipes up... Why do I think doors shouldn't be shootable...
Well... If the map design is done in such a way that you only need to use a keyed door to progress to the next bit then most of the doors will be keyless, therefore no need to shoot them open. There may be some special instances where this is not the case but I think doors should be indestructable for the sake of level design.

Rims there's a load of fields missing off the player type and I can't for the life of me get it working!

Also, you do realise there is already a type for inventory, the pickedup type.

Check out the keycheck function
Function keycheck(player,keyid%)
	For p.pickedup = Each pickedup
		If p\player = player And p\obj =keyid Then Delete p:Return True
	Next
	Return False
End Function


So I would have assumed you'd use the pickedup type to store inventory items but just extend it a little to deal with weight and stuff, then all the keys would automatically appear in the inventory without needing to code anything. I think you've got a very complicated route there Rims. You might want to revisit your code.

Oh, and I still can't get it working.

oops, sorry:
	
	Field itemsCarrying,maxWeight,weight
	Field inv.invItem[20]
	Field showingInv,over,overWait


Damn... Always posting at the same time as you guys! Look up ^^

Also look in the script for adding keys and health to inventories.

			Case "addkey"
				Pv.pickedup = New pickedup
				Pv\player = pl\id ; allowing for multiple players
				Pv\obj=Int(si\param)+2				

			Case "addcredit" ; TODO: Credits! (done!)
				givePlayerCredits(pl\id,Int(si\param))

			Case "addhealth"				
;				pv.pickedup = New pickedup
;				pv\player = pl\id ; allowing for multiple players
;				pv\obj = 2
				Player_GiveHealth(pl,50)


Updated zips.

Code and GFX are updated.

You might want to look at the code and see what's going wrong... I need to take a break! :)

24-10-04 :
Rob:
- Updated player movement/straife fire so it's better!
- New graphics for barrel and exploding barrel
- Updated test map
- Updated tile graphics
- Switches
- Updated grenade laucher so you can shoot grenades over barrels... however, you can shoot through doors too! :/
- Updated the on action trigger points to check under you player as well as infront of it, this means you can't activate an intex system unless you're looking at it.
Mark:
- Editor Updates
Rims:
- Inventory system... but I can't get it to work


Rooms, yup, I was thinking about allowing more internal squares, but someone else can code that. ;-)

Half height collisions I like. Windows would be a neat use for this (either attack a room with a full frontal assault, or sneak round the back and lob a grenade through the window). I suspect the best means of achieving this is some further info in the collision layer. Maybe convert it to a bitwise indicator? Again, not likely to get a chance to do this if I'm working on objects / objectives.

Doors, I also agree that you shouldn't in general be able to destroy doors. However, it might be nice to keep the generic doors that will open without any key as destructible - if only as a means of penalising trigger happy players! Also allows for more interesting scenarios in levels - e.g. make the player think they need a key that they can't find, when actually the only way through is by force. (we'd have to drop a few hints to make this fair of course! But I like games that lie!)

Objects: having the script in a lookup OBJ file or in the map layer itself makes little difference. Other than that the map layers are all ints at the moment, not strings. So a part of my reasoning was based on a 'minimal change' ethic. The template bit is a nice to have, but potentially a very useful one IMHO when we design levels.

Anyway, which bit are you saying is over-complicated? The OBJ lookup file compared to converting the existing OBJ layer to a script layer (just two ways of looking at the same thing really)? The idea of a template OBJ file for creating level specific objects (a handy shortcut, but admittedly not a necessity)? Objects whose state you can switch on, of, and back again (One thought I had was switches which might turn lights on / off in a given area, and have enemies that do / don't like the light / are killed by the light). The If construct? On the latter, I really like the idea of an If construct. For example, this could enable levels where you have to shut down the reactor by doing four separate things. Or, if we have NPCs in some form, more complex conversations based on actions you've performed.

Anyway, I'm not likely to make a real start on this aspect for a day or two, so we've got some time to thrash it out!

The bit I thought that was getting complicated is the variable thing. I like the idea, don't get me wrong, but I'm unsure of how much use it will get and how much time it will take to implement.

I think we're experiencing major feature creep here and not really moving forward. I think we need to get what we've got rock solid so we can start building levels. Then if necessary build in extra bits and pieces but for now I think we've got more than enough features.

I've still got a load of work to do on the alien AI and class stuff to get that 100%.

We need to make a decision on whether to make the guns soft coded. It'll be very difficult to soft code the weapons as they all do different stuff. If we opt for the hard coding method there is a load of weapon code that can just be removed as the weapons are sort of half hearted soft/hard coding and therefore over complicated for hard coded.

Also on the weapons front I think we need to reduce the amount of keys each player has, I think we should just have weapon up and weapon down instead of choosing by number. This is pretty tricky now from what I was saying about the soft/hard coding of the weapons and everything is named by a string rather than number. We need to make weapons decisions, discuss.

There's a bug on the HUD in 2 player mode. Speaking of the HUD, we need to sort this, the bullet thing just isn't working for me and certainly doesn't work with 2 players.

The Half Height collisions I've fudged in using existing map information as I said, the problem is that you can throw grenades through doors as they are the same as barrels or whatever as far as map information goes. If all the doors were -1 hit points then this problem would be solved. Hence my comments earlier.

The other problem I have with the shooting doors is that there's no real feedback when shooting the doors that you're actually making a difference. Also the doors are not a single object and should be, they should share hit points, I think this will be damn hard to implement though so I think we should just make them indestructable and solve any problems.

Mark, I'm still not really following where you're going with the map editor, likewise with alien placement and respawns I really don't like these as objects in the animstrip. You've got to explain this whole object thing to me better!

Basically you know what I think we need; to be able to assign a unique script to a map square without having to have loads of items in the gfx strip. The way I see it there should be only one graphic in the anim strip that is not used in the game, it'll be "Special", and Special will have switches, re-spawners, player start points, alien spawn points etc etc. So if you place an object you can assign a script to it, specials won't be seen it the game and can have any script behind it.

Right... I think that's about it!

Do you like the flying barrels!?

Basically you know what I think we need; to be able to assign a unique script to a map square without having to have loads of items in the gfx strip. The way I see it there should be only one graphic in the anim strip that is not used in the game, it'll be "Special", and Special will have switches, re-spawners, player start points, alien spawn points etc etc. So if you place an object you can assign a script to it, specials won't be seen it the game and can have any script behind it.


The problem with this is that it means you have to write the code for alien spawn, etc, every single time you place a 'Special' icon. While I think we've come to the same conclusion (albeit by different paths!) that this is a necessary evil for switches, I'm less convinced for alien spawn points and player starts.

Player starts should be generic objects (they always do the same thing). Alien spawn points would be very neatly served by template objects, such that the editor user can mostly drag and drop spawn points, but handcode the odd one that they want to be different. Part of my reluctance on this one is that I don't like continuous alien spawn point being invisible and indestructible in the game. I'd much prefer for them to relate to scenery. e.g. 4 aliens some out of a crack in the wall, but no more. Aliens keep coming out of a teleporter until you destroy it. This way we can also add bonus stuff (I'm thinking medals) on completion of a level for destroying all aliens - something you cannot do with continuous invisible indestructible re-spawners! Obviously if we decide this is the right way to go, then spawn points WILL have a dedicated image in the animstrip...

The other problem is that some specials - notably switches - DO have an in game image that directly relates to the object, and one that you may want to change once the script has been triggered. I still think the neatest way to achieve that is to hold the image in the object definition and anim strip.

And yes, variables and if blocks are a bit more advanced than I originally thought we'd go with scripts. I think I'll park that idea for the future, as it could be really cool, but isn't 100% necessary for a first cut of the game. I'm also not convinced it's that hard, but agree it's not where we need to expend effort right now!

Weapons, I think it's reasonable to leave their behaviour hardcoded. But you could leave names configurable, as it's conceivable different game modules could use the same weapon behaviour, but different weapon / bullet gfx and names. e.g. WWII levels, compared to Breed levels could use machine gun style weapons, but would want different names.

As for flying barrels, sorry, didn't even get a chance to d/l the latest code last night!

Oh, and I definitely agree on scope creep. I think I might park this whole objects issue for now and sort out level objectives first, if that's okay? At least then we can create a semblance of a game, albeit without 'puzzles' in it...

Alien spawn points need to be flexible as we don't know what aliens will be available, and creating a new object every time we create a new alien is madness. We should be able to set a trigger as Spawn [alienname]. Job done.

You wouldn't have to write code every time you place a special as you'd just point it at a script number.

Are we agreed on the other types of object then (switches, generics and player starts), and that alien spawn points are our main concern?

Agree, spawn points need to be flexible. But not just in terms of type of alien, also frequency (once, every 5 seconds, up to 10 aliens on screen from this spawn point, total of four aliens max). Which implies that you can't just drop a special and point it at one script - you're likely to be dropping a special and writing a dedicated script for each special. And that's not so different to creating an object for each...

Thinking about it some more, I'm going back towards the idea of parameterised objects again. So we have a generic object that is a spawn point, which takes as parameters the alien type and the frequency. Would that work?

> So we have a generic object that is a spawn point, which takes as parameters the alien type and the frequency. Would that work?
Yep.

I'm still not understanding fully where you're coming from with this so you may as well run with it regardless!

field num_AlienA,num_AlienB
field AlienAHertz,AlienBHertz

or:

field num_Alien%[10]
field AlienHertz%[10]

Okay, a final draft, from a user POV:

Generic objects live in default.obj, and apply to all levels. These appear in the editor palette as currently, and operate exactly as currently, and include any object where the image and script are identical in all levels. For example health paks, player start points, keys, doors. A maximum limit is applied to this file (say 100). RMB on the tile in the palette (NOT on the map) allows you to edit the generic script.

'Special' objects are created by selecting the 'Special' tile object in the editor palette and pasting it on the map. This assigns a new object ID to that tile (>100), and creates a new object of that ID in LEVEL.OBJ. That new object has an icon of 'Special' and no scripts. The new object ID is stored in the map layer as currently.

RMB on the 'special' object on the map (NOT on the palette) allows you to either re-assign the selected map tile object to an existing object ID(allows shared code for samey spawn points, or for say four laser beam tiles across a corridor), OR edit the object. Editing the object allows you to amend the tile image (allows for switches, visible pressure pads, laser beams), and write object specific script(s).

The game engine changes are now very small - ensure level.obj gets loaded if present alongside default.obj. And that's it, because the id's in the map layer will just point to the relevant loaded object.

Q: Why is this better than a script layer in the map?
A: It allows shared code between objects.

Q: Isn't this exactly the same as Rob's original option 1?
A: Almost. But it allows you to have images assigned to the object too. ;-) Oh, and it largely reuses the existing data structures.

Q: Couldn't you do the same with a separate script layer and an object image layer?
A: Yes. But then you've separated the image and the script, and that just feels wrong and prone to error. It also makes the default objects that bit more complex than they need to be.

Q: Why not parameterise?
A: While it would be neat in terms of allowing us to have a single 'spawn' object that could then be driven by inputs, the reality is that most spawn points are going to vary in nature. And it's not that much effort to drop a special on the map, open the script editor and type "createalien(hugger)". And if you really want to, within any one level you can still reuse spawn point code...

Q: Why are my posts so long?
A: Because.

Mark, that sounds spot on to me matey! I'll be expecting it by the end of the day! ;)

That sounds like it's going to solve all the current problems (and a few future problems) we've got and allow for expansion.

I'll get back to work on the alien classes and updated the alien AI this evening. I'll probably do some more graphics too!

Phewee!

(Oh, and I'll stick a TODO: in for Ifs and variables too, I can think of some really cool uses for that idea!)

As I said, run with it, I'm sure it'll work out in the end!!

Rob, can I suggest you remove the latest history from the top post (it's out of date so quick, and duplicated on the download site). Also, dates on the download site against each file would be handy - is there any Javascript or whatever that could automate that for you? i.e. show the file times of the files on your server without you having to edit them each time?

No the NTL site doesn't support anything, which sucks... I wanted to PHP it so I didn't have to worry about dates, file sizes and all that lot, however, it doesn't do anything... that said... it's free bandwidth.

It's starting to take around 60 seconds on my dialup just to load the topic. Is it time for Topic 8?

oops, my fault I think. ;-)

I'll do a topic 8 as soon as I upload something new!

Well, tonight I split the objects into two files, and sorted loading / saving of them. You can still only place default objects though - level specific special tile placement will be next. And then the object assignment / editor stuff.

I won't post updates here though, as I'll continue work on them over the next few days

Just a quickie to say that the object stuff is coming along nicely. I also get some of Rob's misgivings about the approach now that I see how the object palette was working - i.e. simply drawing images from the animstrip. I've changed this to draw the correct image as defined for the object id in question, so there's no longer a 1 to 1 relationship. In addition, I've amended the code that decides whether to show an object image in the engine. In the object definition, you now use a negative image tile (same numbers) to indicate a tile shown in the editor but not in the game.

Whilst there, I've also merged the drawmap_indeditor and drawmap functions back together - it seemed superfluous, and dangerous that they were separate.

What's left? a map conversion routine to convert test.map object layer to the new default object IDs and level specific object IDs. Then prove I haven't broken anything(!). That's tonight's plan, and if successful, I hope to post the results here. And then it's onto actually placing and editing of new level specific objects.

Just so you know I've been working on player and alien stuff, adding more stuff into the class.txt files for these to make eveything better! Players now have the location offset of the gun barrel so bullets come out of the gun now rather than out of their chest. I'm attempting to improve the alien AI although they still seem to love getting down and break dancing, especially when there are 2 players, they get very confused!

Need to do a bit of an overhaul of the players and alien bb files... Is this going to affect your stuff Mark?

Oh, and seeing as Rims appears to have gone AWOL I might have to re-do his inventory stuff, I cannot for the life of me get it to work and I think it's way too complex for what we need. And he's created additional types where we already have an inventory type (as mentioned above).

Also I've drawn some trees and other detail objects.

Oh, an idea I had for the editor...

Have prefab shapes, so for example you might have a 4 x 4 grid of tiles to build up one shape, or the beds, or the desks etc. If we could have a file for each of these just holding the size of the shape (ie 3x2), the layer it belongs to (base/detail etc) and the tiles to use we could then just pick a prefab and splat it in. Just a thought.

And if anyone's noticed, I've updated the screenshots!

So far, I've hit editor.bb hard (obviously!) so don't go anywhere near that. map.bb is taking a bit of a pounding too, although that's less problematic. There are a few other tweaks here and there, but nothing major - am keeping a note of these so as I can post changes. In fact, that's part of the plan for tonight - to get all the basics done, so as all I need change after tonight is the editor.

As for the inventory, I'm tempted to say leave it for now, and give Rims a chance to look into it - it's not *the* most critical thing right now.

I did wonder about shapes - I think the rooms stuff holds the basis for this. i.e. it should be possible for some 'rooms' (or 'shapes' now) to be 1x2, 2x1, 2x2, 3x3, whatever. And that some shapes are a fixed size, but others are expandable (like rooms are currently). But then I think we're on to yet another 'nice to have' feature...

...what I'd really like is for us to be at a point soon where we can truly start asking people to make levels for the game. For example through the Blitz newsletter, and potentially through AB fan sites (although I think that comes later). I think we're very nearly there now - pretty much just objects, level objectives, your re-org, and probably a few more script commands / triggers).

...and then we'll not just be playing catch-up and scope creep with our own ideas, but everyone else's too...

Hey there peeps,

How about some sort of online score board. Im pretty sure most people here are going to download it as well as in other communities and it would be a nice bit of friendly banter.

Especially between all of you writing the game, I expect you are all getting fairly good at it now ;)

I expect you are all getting fairly good at it now
Not really! We've only got one test level and I doubt we'll be writing the rest of the levels!

An online score board is a nice idea... It's something we can do at the end.

We've put a stop to features now, we've got a list as long as your arm of things to get in. Once we've got that lot in, a couple of test levels and a compiled version out to test I'm sure we'll get another 2000 feature requests in!

Feature requests are now officially on hold!

I'll write up a new ToDo list that we can go through and cross off each item.

Rob, stick 'em on the online list, I can extract a more readable list off there as a one off. I already added the shapes stuff for the editor.

We may want to re-prioritise some items already there. I suggest:
High means it must be done before we 'publish' a first version and a couple of test levels. Inviting comment & levels, and feature req's. Afte publishing, this category can be reserved for real nasty bugs we need to squish. e.g. objectives, etc.
Medium means features (and the odd bugette no doubt) that ought to be done before we can claim to have finished. e.g. more tiles / aliens
Low means neat ideas. That will probably never get done, but are neat enough to warrant remembering. e.g. my If / variables stuff for scripting

player.bb->keyCheck()
Function keycheck(player,keyid%)
	For p.pickedup = Each pickedup
		If p\player\id = player And p\obj =keyid Then Delete p:Return True
	Next
	Return False
End Function

Remove the pickedUp type from the util.bb include
If you run it, you'll be prompted to change something in the script.bb file. This is:
			Case "checkkey"
			   	If Not keycheck(pl\id,Int(si\param)+2) Then ClearScriptFrom(si):Return

			Case "addkey"
				InvItem_Create(Int(si\param)+2,0,pl,objects,1)			

			Case "addcredit" ; TODO: Credits! (done!)
				givePlayerCredits(pl\id,Int(si\param))

The "InvItem_Create(Int(si\param)+2,0,pl,objects,1)" function adds an object to your inventory. You can specify the weight and obj number as well as the image and frame to use to draw it in the inventory.

Which just changes the pv\player. Instead of just passing the player ID (which we don't really need), we pass the actual object.

I've added these fields to the player type:
	Field itemsCarrying,maxWeight,weight
	Field inv.pickedup[20]
	Field showingInv,over,overWait

Add this to the engine.bb->draw() function, wherever you want:
drawBothInventories()

I've temporarily added this to the engine.bb->loop function to open the inventory for player one on a toggle:
If KeyHit(57) Then showInventory(First player)

Use this to set the player weights on the player creation:
setPlayerInvLevels(p.player,maxWeight)

If you don't want to use the weight feature, make everything you pick up weigh 0.
here's a revised inventory.bb:
; inventory
Const INVITEM_WIDTH = 70

Type pickedup
	Field player.player
	Field obj,weight
	Field x,y,image,imageFrame	
End Type


Function useInventoryItem(p.player,i.pickedup)
End Function


Function InvItem_Create(obj,weight,p.player,image,frame)
	i.pickedup=New pickedup
	i\obj=obj
	i\weight=weight	
	i\image=image
	i\imageFrame=frame
	addInvItem(p,i)		
End Function

Function setPlayerInvLevels(p.player,maxWeight)
	p\maxWeight=maxWeight
	p\weight=0
	p\itemsCarrying=0	
	p\showingInv=0
	p\overWait=MilliSecs()
End Function

Function showInventory(p.player)
	p\showingInv=1-p\showingInv
End Function

Function UpdateInventories(p.player)
	; this handles the controls
	If p=First player
		max=(GraphicsWidth()/2)-10
	Else
		max=(GraphicsWidth())
	EndIf
	
	If (MilliSecs()-p\overWait) > 200
		If control(pright,p\id)
			p\over=p\over+1
			p\overWait=MilliSecs()
			
		ElseIf control(pleft,p\id)
			p\over=p\over-1
			p\overWait=MilliSecs()
		EndIf	
	EndIf
	If p\over<0 Then p\over=p\itemsCarrying-1
	If p\over>p\itemsCarrying-1 Then p\over=0
End Function

Function addInvItem(p.player,item.pickedup)
	If p=Null Then Return
	
	If item= Null Then
		DebugLog "Inv Item is NULL"
		Return		
	EndIf
	
	If p\weight+item\weight <= p\maxWeight
		; pickup
		item\player=p
		p\inv[p\itemsCarrying]=item
		p\weight=p\weight+item\weight
		p\itemsCarrying=p\itemsCarrying+1
		refreshInvItems()	
	Else
		DebugLog "Cannot pick up "+item\obj+". It's too heavy"
		Return
	EndIf	
End Function

Function refreshInvItems()

	For p.player=Each player
		If p = First player
			min=5
			x=min
			max=(GraphicsWidth()/2)-10
		Else
			min=(GraphicsWidth()/2)+10
			x=min
			max=(GraphicsWidth())
		EndIf
	
		y=10
		If p\itemsCarrying>0
			For i=0 To p\itemsCarrying-1
				inv.pickedup=p\inv[i]
				If inv<>Null								
					If x > max-INVITEM_WIDTH
						x=min
						y=y+INVITEM_WIDTH
					EndIf
					inv\x=x
					inv\y=y	
					x=x+INVITEM_WIDTH
				EndIf
			Next
		EndIf
	Next
End Function

Function drawInvItems(p.player)
	If p\showingInv=0 Then Return
	
	If p=First player
		max=(GraphicsWidth()/2)-10
		; player one
		Color 150,150,150:Rect 0,0,max,GraphicsHeight()
		Color 200,200,200:Rect max,0,1,GraphicsHeight()
		;btext 10,GraphicsHeight()-10,p\weight+" / "+p\maxWeight
	Else
		max=(GraphicsWidth()/2)-10
		Color 150,150,150:Rect max,0,GraphicsWidth()/2,GraphicsHeight()
		Color 200,200,200:Rect max,0,GraphicsWidth()/2,GraphicsHeight()
		;btext (GraphicsWidth()/2)+10,GraphicsHeight()-10,p\weight+" / "+p\maxWeight
	EndIf

	If p\itemsCarrying>0
		For i=0 To p\itemsCarrying-1
			inv.pickedup=p\inv[i]
			If inv<>Null
				If inv\image<>0
					DrawImage inv\image,inv\x,inv\y,inv\imageFrame
				EndIf
				Color 255,255,255
				Rect inv\x,inv\y,INVITEM_WIDTH,INVITEM_WIDTH,0
				Color 0,0,0
				;bText inv\x+INVITEM_WIDTH/2,inv\y+INVITEM_WIDTH/2,inv\obj,1,1
				If i=p\over
					Color 255,0,0
					Rect inv\x,inv\y,INVITEM_WIDTH,INVITEM_WIDTH,0
				EndIf
			EndIf
		Next
	EndIf	
End Function

Function drawBothInventories()
	drawInvItems(First player)
	drawInvItems(Last player)
End Function

Hope you can get it working. I'll get to work on the hud. I've got very very very very little spare time, but I'm hoping to get something done. I'm not ignoring this project, guys.

Here's hoping that lot works for Rob, looks like good stuff! Although it may turn out to do more than we currently need, I'm quite sure we'll find a use for it!

Anyway, here are my editor updates. This is now at a stage where default objects work in the editor and game. level specific objects work in the game, but cannot yet be PLACED in the editor (that's the next bit to be done!). I do think this is worth updating in the common code, as it's mostly there, and I shouldn't now need to hit the game code at all. (well, actually I reckon I can improve the speed of draw(), but that's another matter altogether...)

Okay, here goes. First, a replacement default.obj file
total objects = 32

image = 0
trigger = onover()
description = Ammo
addammo(50)|remove(me)

image = 1
trigger = onover()
description = Health
addhealth(10)|playsoundonce(health_pickup)|remove(me)

image = 2
trigger = onover()
description = Key (yellow)
addkey(1)|playsoundonce(key_pickup)|remove(me)

image = 3
trigger = onover()
description = Key (red)
addkey(2)|playsoundonce(key_pickup)|remove(me)

image = 4
trigger = onover()
description = Key (green)
addkey(3)|playsoundonce(key_pickup)|remove(me)

image = 5
trigger = onover()
description = Key (blue)
addkey(4)|playsoundonce(key_pickup)|remove(me)

image = 6
trigger = ontouch()|ondestroy()
description = Door (yellow)
checkkey(1)|opendoor()|remove(nearby)
opendoor()|remove(nearby)

image = 7
trigger = ontouch()|ondestroy()
description = Door (yellow)
checkkey(1)|opendoor()|remove(nearby)
opendoor()|remove(nearby)

image = 8
trigger = onover()
description = 100 Credit
addcredit(100)|remove(me)

image = 9
trigger = onover()
description = 50 Credit
addcredit(50)|remove(me)

image = 10
trigger = onover()
description = 10 Credit
addcredit(10)|remove(me)

image = 11
trigger = ontouch()|ondestroy()
description = Door (red)
checkkey(2)|opendoor()|remove(nearby)
opendoor()|remove(nearby)

image = 12
trigger = ontouch()|ondestroy()
description = Door (red)
checkkey(2)|opendoor()|remove(nearby)
opendoor()|remove(nearby)

image = 13
trigger = ontouch()|ondestroy()
description = Door (blue)
checkkey(4)|opendoor()|remove(nearby)
opendoor()|remove(nearby)

image = 14
trigger = ontouch()|ondestroy()
description = Door (blue)
checkkey(4)|opendoor()|remove(nearby)
opendoor()|remove(nearby)

image = 15
trigger = ontouch()|ondestroy()
description = Door (green)
checkkey(3)|opendoor()|remove(nearby)
opendoor()|remove(nearby)

image = 16
trigger = ontouch()|ondestroy()
description = Door (green)
checkkey(3)|opendoor()|remove(nearby)
opendoor()|remove(nearby)

image = 17
trigger = onaction()
description = Intex System
intex()

image = 18
trigger = onaction()
description = Intex System
intex()

image = 19
trigger = onaction()
description = Intex System
intex()

image = 20
trigger = onaction()
description = Intex System
intex()

image = 21
trigger = onover()
description = weapon (Broadhurst DJ Twinfire 3LG)
pickupweapon(1)|remove(me)

image = 22
trigger = onover()
description = weapon (Dalton Arc Flame)
pickupweapon(2)|remove(me)

image = 23
trigger = onover()
description = weapon (Robinson Plasma Gun)
pickupweapon(3)|remove(me)

image = 24
trigger = onover()
description = weapon (Ryxx Firebolt MK22)
pickupweapon(4)|remove(me)

image = 25
trigger = onover()
description = weapon (Styrling Multimatic)
pickupweapon(5)|remove(me)

image = 26
trigger = onover()
description = weapon (High Impact Astro Laser)
pickupweapon(6)|remove(me)

image = -33
trigger = onload()
description = player one start position
positionplayer(1)|remove(me)

image = -34
trigger = onload()
description = player two start position
positionplayer(2)|remove(me)

image = 35
trigger = ontouch()|ondestroy()
description = no key door (horizontal)
opendoor()|remove(nearby)
opendoor()|remove(nearby)

image = 36
trigger = ontouch()|ondestroy()
description = no key door (vertical)
opendoor()|remove(nearby)
opendoor()|remove(nearby)

image = 37
trigger = ondestroy()
description = Exploding Barrel
explode()|remove(me)|remove(collision)

Now a test.obj for other bits used by test.map
total objects = 6

image = -27
trigger = onload()
description = alien spawn point (normal alien) [normal?]
createalien(alien)|remove(me)

image = -28
trigger = onload()
description = alien spawn point (hugger)
createalien(hugger)|remove(me)

image = -29
trigger = onevery(10)
description = every 10 seconds create an alien
createalien(alien)

image = -30
trigger = onevery(30)
description = every 30 seconds create an alien
createalien(alien)

image = -31
trigger = onevery(10)
description = every 10 seconds create a hugger
createalien(hugger)

image = -32
trigger = onevery(30)
description = every 30 seconds create a hugger
createalien(hugger)

Next, you'll need to fix any maps you have by running this. Note the output goes to out.map. Just rename out.map to test.map and you'll be sorted - ignore the out.obj, it's a copy of test.obj.
Include "inc\consts.bb"
Include "inc\util.bb"
Include "inc\sounds.bb"
Include "inc\map.bb"

DebugLog "Loading..."
load("maps\test")

DebugLog "Converting..."
For x=0 To 255
	For y=0 To 255
		obj=map(x,y,LayerObject)
		Select obj
			Case 28 : obj=MAX_DEFAULT_OBJECTS+1
			Case 29 : obj=MAX_DEFAULT_OBJECTS+1
			Case 30 : obj=MAX_DEFAULT_OBJECTS+1
			Case 31 : obj=MAX_DEFAULT_OBJECTS+1
			Case 32 : obj=MAX_DEFAULT_OBJECTS+1
			Case 33 : obj=MAX_DEFAULT_OBJECTS+1
			Case 34 : obj=28
			Case 35 : obj=29
			Case 36 : obj=30
			Case 37 : obj=31
			Case 38 : obj=32
		End Select
		map(x,y,LayerObject)=obj
	Next
Next


DebugLog "Saving..."
save("maps\out")

End

Replace editor.bb with this
; blitz3d version

; keys

; arrow keys move around map
; pgup/pgdown scroll through tiles
; return - pick up tile
; numpad4 - place a ground of 4 squares starting with the selected tile ie 12
;                                                                          34
; Space - Promote background tile to a over block tile

Include "inc\scancodes.bb"
Include "inc\sounds.bb"
Include "inc\consts.bb"
Include "inc\gui.bb"
Include "inc\map.bb"
Include "inc\editor include.bb"
Include "inc\util.bb"
Include "inc\editor-room.bb"
Include "inc\editor-undo.bb"

; set up graphics
Graphics 800,600,32,0
SetBuffer BackBuffer()
loadGraphics() ; both version use this.
;had trouble when moving these to gui (symbFont did not exist for some reason)
Global font = LoadFont("Arial.ttf",15)
Global symbFont = LoadFont("Symbol.ttf",15)
InitGUI()

; check to load a level from the command line
If Len(CommandLine())
	load(CommandLine())	
Else
	createRandomMap()
EndIf

Global LastInput = MilliSecs()
Global sc_x=0
Global sc_y=0
Global scrolldelay = 0
Global mousescroll=False
Const setscrolldelay = 2
Global activelayer = 1
Global activetile = 0
Global tileoffset = 0
Global xpos
Global ypos
Global current_tool=1 ; 1=tile tool, 2=room tool
Global coll_tool=1

visible(LayerCollision)=0
visible(LayerHits)=0
refreshRoomList()
quickLoadRoom("maps\rooms\default.room")
statusMsg("Ready...")

main()
End

;---------------------------------------------

Function main()
	Repeat
	
		; very first thing we do is a clear screen!
		Cls
		
		; now work out where the mouse is : over the map or tileset / palette
		MouseOverMap	 = False
		MouseOverTileset = False
		If (MouseX()<640 And MouseY()<480) Then 
			MouseOverMap=True
		ElseIf (MouseY()>490 And MouseY()<586) And (MouseX()>32 And MouseX()<768) Then
			MouseOverTileset = True 
		End If
	
		; deal with the common GUI elements (the RHS of the screen)
		handleGUICommon()		
		
		; next deal with the main editing area of the screen
		Select current_tool
			Case 3
				Color 255,255,255
				Text 50,50,"An integrated tile editor here would be neat..."
				
			Case 6
				Color 255,255,255
				Text 50,50,"Define objectives here, with ID numbers"			
				Text 50,65,"Need to define what constitutes completion for each"				
				Text 50,80,"Each objective can have a script assigned to run on completion, which might use new commands like:"				
				Text 50,95,"startlevel(basement), givelevel(nebraska), giveitem(medal),giveobjective(12,mandatory)"				
				Text 50,110,"Assign at least one objective as mandatory to each player"
				Text 50,125,"Additional optional objectives can also be assigned to each"	
				Text 50,140,"Note that not all objectives need to be assigned at the start"							
				Text 50,155,"some can be gained by completing other objectives, some by object scripts"				
				
			Case 7
				Color 255,255,255
				Text 50,50,"Define level settings here, such as:"			
				Text 50,65,"tilesets to use, object file to use"				
				Text 50,80,"info to show on level select screen (image, name, text, map location xy)"				
				Text 50,95,"background music to play"				
				Text 50,110,"movie / image / text to show on starting level"
				
			Default
				handleMap(MouseOverMap)	
		End Select
			
		; ******************************** TOOL SPECIFIC GUI
			
		If current_tool=1 Then ; TILE TOOL
		
			; map stuff
			If MouseOverMap Then
				; tile highlighter
				Color 0,255,0
				Rect (xpos-Floor(sc_x/32))*32,(ypos-Floor(sc_y/32))*32,32,32,False	
										
				Select activeLayer				
					Case LayerCollision,LayerHits
						handleMapCollision()	
					Case LayerBase,LayerDetail,LayerBlock,LayerObject,LayerTop
						handleMapTile()		
				End Select
			EndIf			
			
			; tile tool GUI
			handlePalette(activelayer,MouseOverTileset) 
			
		ElseIf current_tool=3 Then ; TILE EDITOR
					
			; TODO: Tile Editor!
					
		ElseIf current_tool=2 Then ; ROOM TOOL
			handleRoom()	
			handlePalette(LayerBase,MouseOverTileset) 				
				
		ElseIf current_tool=4 Then ; FILE MENU
			handleFileMenu()
			
		ElseIf current_tool=5 Then ; EXIT MENU
			If Button(650,240,140,"Confirm?",False,False,KEY_NONE,"Click to exit the editor") Then Exit
			
		ElseIf current_tool=6 Then ; Objectives editor
		
			; TODO: Level objectives editor
		
		ElseIf current_tool=7 Then ; Level Settings editor			

			; TODO: Level settings editor
			
		EndIf     
		
		;******************** SCREEN UPDATE
		drawStatusBar()
		DrawToolTip()
		DrawImage cursor,MouseX()-1,MouseY()-1
		Flip
	
	Forever
End Function

Function handleGUICommon()
	;*************************** core GUI elements
		
	; layers / visibility
	For n=0 To NumLayers
		If current_tool=1 And activelayer=n Then act=True Else act=False
		If Button(650,n*20,100,layername(n),act,False,n+2,"Edit this map layer") Then
			activelayer=n				
			If activeLayer = LayerObject Then tileoffset = 0
			If activelayer = LayerHits Then coll_tool=4
			If activelayer = LayerCollision Then coll_tool=1
			current_tool=1
		EndIf
		
		If Button(760,n*20,30,"Vis",visible(n),False,KEY_NONE,"Show / Hide this layer on the map")
			visible(n)=(visible(n)+1) Mod 2
			Repeat:Until MouseDown(1)=False
		EndIf
	Next		
		
	; other drawing tools
	If Button(650,140,140,"Room Tool",current_tool=2,False,KEY_NONE,"Create and draw rooms") Then current_tool=2	
	If Button(650,160,140,"Tile Editor",current_tool=3,False,KEY_NONE,"Edit tiles") Then current_tool=3	
	If Button(650,180,140,"Objectives",current_tool=6,False,KEY_NONE,"Define level objectives") Then current_tool=6
	If Button(650,200,140,"Level Settings",current_tool=7,False,KEY_NONE,"Define level settings") Then current_tool=7	

	If Button(650,220,68,"File Menu",current_tool=4,False,KEY_NONE,"Load, save or create a new level") Then current_tool=4
	If Button(723,220,68,"Exit",current_tool=5,False,KEY_NONE,"Exit the editor") Then current_tool=5		
	
	ry=GraphicsHeight()-140
	If First undoTile<>Null ;Undo tools
		If Button(650,ry,68,"Undo",False,False,KEY_NONE,"Undo all changes up to last reset")
			doUndo():Delay 200
		ElseIf Button(723,ry,68,"Reset",False,False,KEY_NONE,"Reset undo history")
			clearUndo():Delay 200
		EndIf	
	EndIf
End Function

Function handleMap(mouseover)
	;********************* MAP
	; draw the selected layers of the map
	For n=1 To NumLayers
		If visible(n)=1 Then drawmap(sc_x,sc_y,n,True);drawmap_inEditor(sc_x,sc_y,n)
	Next
	If visible(LayerCollision)=1 Then drawmap(sc_x,sc_y,LayerCollision,True) ; needs to be drawn last!
			
	; find mouse location on map
	If mouseover Then
		xpos = Floor(MouseX()/32)+Floor(sc_x/32)
		ypos = Floor(MouseY()/32)+Floor(sc_y/32)
		Button(2,510,28,xpos)
		Button(2,530,28,ypos)			
	Else
		xpos = -1
		ypos = -1
	EndIf

	;******************* INPUT HANDLING

	; map movement code (arrow keys and mouse near edge)
	key_left=False:key_right=False:key_up=False:key_down=False	
	If mouseover Then 
		If MouseX()<5 Then key_left=True
		If MouseX()>635 Then key_right=True
		If MouseY()<5 Then key_up=True
		If MouseY()>475 Then key_down=True		
		If key_left Or key_right Or key_up Or key_down Then
			If Not mousescroll Then mousescroll=True:scrolldelay=setscrolldelay
		Else
			mousescroll=False
		End If
	EndIf
	If KeyDown(KEY_ARROW_RIGHT) Then key_right=True
	If KeyDown(KEY_ARROW_LEFT) Then key_left=True
	If KeyDown(KEY_ARROW_UP) Then key_up=True
	If KeyDown(KEY_ARROW_DOWN) Then key_down=True
	
	; slow down the map scrolling a bit
	If scrolldelay < 0
		If key_right And sc_x<7552 Then sc_x=sc_x+32 : scrolldelay = setscrolldelay
		If key_left And sc_x>0 Then sc_x=sc_x-32 : scrolldelay = setscrolldelay
		If key_up And sc_y>0 Then sc_y=sc_y-32 : scrolldelay = setscrolldelay
		If key_down And sc_y<7552 Then sc_y=sc_y+32 : scrolldelay = setscrolldelay
	Else
		scrolldelay = scrolldelay - 1
	EndIf
End Function

Function handleMapCollision()

	If MouseDown(1) Then 
		Select coll_tool
			Case 1; static
				undoTile(xpos,ypos,map(xpos,ypos,LayerHits),map(xpos,ypos,LayerCollision))
				;if left control is held
				If KeyDown(KEY_CTRL_LEFT) Then	
					map(xpos,ypos,LayerCollision)=czVisStatic 
				Else 
					map(xpos,ypos,LayerCollision)=czStatic			
				EndIf
				map(xpos,ypos,LayerHits)=-1
				
			Case 2; dynamic
				undoTile(xpos,ypos,map(xpos,ypos,LayerHits),map(xpos,ypos,LayerCollision))
				;if left control is held
				If KeyDown(KEY_CTRL_LEFT) Then	
					map(xpos,ypos,LayerCollision)=czVisDynamic 
				Else 
					map(xpos,ypos,LayerCollision)=czDynamic			
				EndIf
				map(xpos,ypos,LayerHits)= lhDefaultHits ;see consts.bb for lhDefaultHits	
				
			Case 3; vis blocker
				undoTile(xpos,ypos,map(xpos,ypos,LayerHits),map(xpos,ypos,LayerCollision))				
				If map(xpos,ypos,LayerCollision)=czDynamic Then	
					map(xpos,ypos,LayerCollision)=czVisDynamic 
				ElseIf map(xpos,ypos,LayerCollision)=czStatic 
					map(xpos,ypos,LayerCollision)=czVisStatic			
				EndIf				
				
			Case 4; hitpoint tool
				undoTile(xpos,ypos,map(xpos,ypos,LayerHits),map(xpos,ypos,LayerCollision))	
				If map(xpos,ypos,LayerCollision)=czDynamic Or map(xpos,ypos,LayerCollision)=czVisDynamic Then
					;if left control is held
					If KeyDown(KEY_CTRL_LEFT) Then IncVal = 5 Else IncVal = 1 
					map(xpos,ypos,LayerHits) = map(xpos,ypos,LayerHits) + IncVal
					;limit the maximum value for a dynamic collisions hit points to 99
					If map(xpos,ypos,LayerHits) > lhMaxHits Then map(xpos,ypos,LayerHits) = 99
					Delay 100 ; not too quick now!	
				EndIf			
		End Select
			
	ElseIf MouseDown(2) Then 
		undoTile(xpos,ypos,map(xpos,ypos,LayerHits),map(xpos,ypos,LayerCollision))
		Select coll_tool
			Case 1,2
				map(xpos,ypos,LayerCollision)=0
				map(xpos,ypos,LayerHits) = 0
			Case 3
				If map(xpos,ypos,LayerCollision)=czVisDynamic Then	
					map(xpos,ypos,LayerCollision)=czDynamic 
				ElseIf map(xpos,ypos,LayerCollision)=czVisStatic 
					map(xpos,ypos,LayerCollision)=czStatic			
				EndIf
			Case 4; hitpoint tool
				undoTile(xpos,ypos,map(xpos,ypos,LayerHits),map(xpos,ypos,LayerCollision))	
				If map(xpos,ypos,LayerCollision)=czDynamic Or map(xpos,ypos,LayerCollision)=czVisDynamic Then
					;if left control is held
					If KeyDown(KEY_CTRL_LEFT) Then IncVal = -5 Else IncVal = -1 
					map(xpos,ypos,LayerHits) = map(xpos,ypos,LayerHits) + IncVal
					;limit the minimum value for a dynamic collisions hit points to 1
					If map(xpos,ypos,LayerHits) < 1 Then map(xpos,ypos,LayerHits) = 1
					Delay 100 ; not too quick now!
				EndIf						
		End Select				
	EndIf	
End Function

Function handleMapTile()
	; Left mouse button handler
	If MouseDown(1) Then
		If activelayer=LayerObject Then
			If activetile<numDefaultObjects					
				map(xpos,ypos,activelayer)=activetile+1
				If activetile = 37 ; give a barrel 5 hit points
					map(xpos,ypos,LayerHits) = 5
					map(xpos,ypos,LayerCollision)=czDynamic
				EndIf
			EndIf
		Else
			undoTile(xpos,ypos,map(xpos,ypos,activeLayer),map(xpos,ypos,LayerCollision))
			map(xpos,ypos,activelayer)=activetile+1					
		End If
	End If		
	
	;Right Mouse Button Layer Handler
	If MouseDown(2) Then
		Select ActiveLayer
		
			Case LayerBase
				undoTile(xpos,ypos,map(xpos,ypos,ActiveLayer),map(xpos,ypos,LayerCollision))
				map(xpos,ypos,activelayer) = 0
										
			Case LayerObject,LayerBlock,LayerDetail
				undoTile(xpos,ypos,map(xpos,ypos,ActiveLayer),map(xpos,ypos,LayerCollision))
				map(xpos,ypos,ActiveLayer) = 0									

		End Select
	EndIf		

; NB: removed promotion of base tile to top tile (SPACE) as they now have different tilesets!

	If KeyDown(KEY_ENTER) Then activetile = map(xpos,ypos,activelayer)-1 ; pick up tile (RETURN)

	If KeyDown(KEY_NUMPAD_4) ; place 4 concurrent tiles in a square (NUMPAD4!)
		; TODO: Undo in here
		map(xpos,ypos,activelayer)=activetile+1
		map(xpos+1,ypos,activelayer)=activetile+2
		map(xpos,ypos+1,activelayer)=activetile+3
		map(xpos+1,ypos+1,activelayer)=activetile+4
	EndIf		

End Function

Function handlePalette(layer,mouseover)
	
	mz = MouseZSpeed()
	
	;select the tileset to show in the palette	
	Select layer
		Case LayerBase, LayerBlock
			tileset=btiles : maxtile=bmaxtile
		Case LayerObject	
			tileset=objects : maxtile=NumDefaultObjects
		Case LayerDetail,LayerTop
			tileset=dtiles : maxtile=dmaxtile
		Case LayerCollision,LayerHits
			If Button(40,510,100,"Static",coll_tool=1,False,KEY_S,"Set tiles to be indestructible walls, vis blocking with Left Control") Then 	coll_tool=1
			If Button(150,510,100,"Dynamic",coll_tool=2,False,KEY_D,"Set tiles to be destructible walls, vis blocking with Left Control") Then 	coll_tool=2
			If Button(260,510,100,"Vis-Blocker",coll_tool=3,False,KEY_V,"Set tiles to be barriers to vis checking") Then 	coll_tool=3	
			If Button(370,510,100,"Hit Points",coll_tool=4,False,KEY_H,"Set tiles to be barriers to vis checking, faster with Left Control") Then 	coll_tool=4								
			Return 
		Default
			Return ; no point hanging around - there's no relevant palette!
	End Select
	
	; active tile	
	Button(770,510,28,activetile+1,False,False,KEY_NONE,"The ID of the currently selected tile")			
	
	;draw the palette itself
	For n=0 To 68
		tileno = n + tileoffset
		If layer=LayerObject Then
			If tileno<maxtile Then
				DrawBlock tileset,((n-(Floor(n/23)*23))*32)+32,490+(Floor(n/23)*32),Abs(objectdef(n+tileoffset+1)\image)				
			ElseIf tileno=maxtile Then
				DrawBlock tileset,((n-(Floor(n/23)*23))*32)+32,490+(Floor(n/23)*32),38 ; 38 is the SPECIAL tile				
			End If
		Else
			If tileno<maxtile Then DrawBlock tileset,((n-(Floor(n/23)*23))*32)+32,490+(Floor(n/23)*32),n+tileoffset
		End If		
		If tileno = activetile Then Color 0,255,0:Rect ((n-(Floor(n/23)*23))*32)+32,490+(Floor(n/23)*32),32,32,False
	Next

	; move around the palette
	tup=0

	;I know it looks like there is nothing in the quotes in the line below, but trust me, there is
	If (Button (0,490,32,"­",False,True,KEY_PAGE_UP,"Scroll up the tileset",False)=True Or (mz = 1 And MouseOverTileset)) And tileoffset > 0 And scrolldelay<0 Then tup=-1
	If (Button (768,490,32,"¯",False,True,KEY_PAGE_DOWN,"Scroll down the tileset",False)=True Or (mz = -1 And MouseOverTileset)) And scrolldelay<0 Then tup=1
	If KeyDown(KEY_NUMPAD_0) And scrolldelay<0 Then activetile = activetile +1:scrolldelay=10
	If KeyDown(KEY_CTRL_RIGHT) And scrolldelay<0 Then activetile = activetile -1:scrolldelay=10
	If tup<>0 Then tileoffset=tileoffset + (tup*23):scrolldelay=15		

	; handle mouse being over the palette
	If mouseover Then
		If MouseDown(1) Then 
			checktile = (Floor((MouseX()-32)/32))+(Floor((MouseY()-490)/32)*23)+tileoffset
			If (checktile < maxtile) And (checktile >-1) Then activetile = checktile
			;If currentRoom<>Null And scrolldelay<0 Then UpdateRoomBlock()	<-removed in changing how room tiles are selected
		ElseIf MouseDown(2)
			If Layer=layerObject And (First objdef<>Null)
				checktile = (Floor((MouseX()-32)/32))+(Floor((MouseY()-490)/32)*23)+tileoffset
				If (checktile < maxtile) And (checktile >-1) Then activetile2 = checktile
				If (activetile2+1) =< numDefaultObjects
					For n=1 To CountString(objectdef(activetile2+1)\trigger,"|")
						trigger$=parse(objectdef(activetile2+1)\trigger,"|",n)
						newdef$ = InputBox("New Script for trigger: '"+trigger+"'. ESC or RETURN to end",20,20,500,300,True,objectdef(activetile2+1)\script[n])
						If Len(newDef) > 0 Then objectdef(activetile2+1)\script[n] = newdef$
					Next														
				EndIf
			EndIf 		
		Else
			; move your mouse over it
			If Layer=layerObject And (First objdef<>Null)
				checktile = 1+(Floor((MouseX()-32)/32))+(Floor((MouseY()-490)/32)*23)+tileoffset
				If (checktile <= maxtile) And (checktile >0) Then 				
					If objectdef(checktile)<>Null
						w=400
						count=CountString(objectdef(checktile)\trigger,"|")
						h=80+(20*(count+1))
						Color 60,60,60:Rect 12,13,w,h,1:Rect 13,13+h,StringWidth("MB2 to edit")+5,FontHeight(),1
						Color 255,255,255 : Rect 10,10,w,h,1 : Rect 10,10+h,StringWidth("MB2 to edit")+5,FontHeight(),1
						Color 0,0,0 : Rect 10,10,w,h,0 : Rect 10,10+h,StringWidth("MB2 to edit")+5,FontHeight(),0
											
						Text 20,20,"Object: "+objectdef(checktile)\name
						Text 20,40,"Image: "+objectdef(checktile)\image
						Text 20,60,"Trigger: "+objectdef(checktile)\trigger
						For n=1 To CountString(objectdef(checktile)\trigger,"|")
							Text 20,80+(n*20),objectdef(checktile)\script[n] 
						Next
						
						Text 12,10+h,"MB2 to edit"
					EndIf
				EndIf
			EndIf
		EndIf	
	EndIf
End Function

Function handleRoom()	
		
		;draw mini room
		rx=GraphicsWidth()-150
		ry=GraphicsHeight()-240
		drawRoom(rx,ry)
						
		; new room
		If Button(GraphicsWidth()-50,ry,48,"New",False)
			createBlankRoom():Delay 200 ; stop fast re-clicking	
				
		; load room					
		ElseIf Button(GraphicsWidth()-50,ry+20,48,"Load",False)
			; load the listbox containing possible rooms.
			num=ListDlg(rx-200,ry-230,180,330,room_list)
			If num<>-1
				which$=SL_Strings(room_list,num)			
				If Instr(which,".room")<>0
					quickLoadRoom(which)
				EndIf
			EndIf
			Delay 200 	
			
		; save room
		ElseIf Button(GraphicsWidth()-50,ry+40,48,"Save",False)
			saveRoom():Delay 200		
		EndIf

		; draw room
		If xpos>-1 Then
			If MouseDown(1)
				If create_a_room_sx=-1 And create_a_room_sy=-1
					;DebugLog "setting up room..."
					create_a_room_sx=xpos
					create_a_room_sy=ypos
				EndIf
			Else
				If create_a_room_sx<>-1 And create_a_room_sy<>-1
					;DebugLog "Creating room..."
					; ready to finish.					
					createRoomHere(create_a_room_sx,create_a_room_sy,xpos,ypos)	
					create_a_room_sx=-1
					create_a_room_sy=-1										
				EndIf
			EndIf	
		Else
			create_a_room_sx=-1		
			create_a_room_sy=-1		
		End If
		
		; draw other GUI elements
		If create_a_room_sx=-1 And create_a_room_sy=-1
			statusMsg("Use LMB on map to start top left corner of a room, and drag and release to size",0)
			; tile highlighter
			Color 0,255,0
			x=(xpos-Floor(sc_x/32))*32:y=(ypos-Floor(sc_y/32))*32
			Line x,y,x+32,y
			Line x,y,x,y+32			
		Else
			Button(2,510,28,create_a_room_sx)
			Button(2,530,28,create_a_room_sy)				
			Button(2,550,28,Int(Abs(create_a_room_sx-xpos)+1))
			Button(2,570,28,Int(Abs(create_a_room_sy-ypos)+1))
			; tile highlighter
			Color 0,255,0
			x=(xpos-Floor(sc_x/32))*32:y=(ypos-Floor(sc_y/32))*32
			Line x+32,y,x+32,y+32
			Line x,y+32,x+32,y+32
			Line create_a_room_sx*32-sc_x, create_a_room_sy*32-sc_y, 32+create_a_room_sx*32-sc_x, create_a_room_sy*32-sc_y
			Line create_a_room_sx*32-sc_x, create_a_room_sy*32-sc_y, create_a_room_sx*32-sc_x,    32+create_a_room_sy*32-sc_y					
		EndIf
		
End Function


Function handleFileMenu()
		; FILE TOOLS
		ry=240
		If Button(650,ry,140,"New",False,False,KEY_F8,"Create a new map") Then createRandomMap()
		
		If Button(650,ry+20,140,"Save",False,False,KEY_F1,"Save the current map") Then
			FileName$ = InputBox("Enter filename to save (without .map extension)",200,300,400,40,True,"maps")
			If Len(FileName) <1 Then Return
			If FileType(FileName) = 2 Then Return
			If Instr(filename,".map") Then Replace(filename,".map","")
			save(filename)
			;Save last file name
			fileout = WriteFile("prefs.txt")
				WriteString fileout,FileName$
			CloseFile(fileout)		
		End If
		
		If Button(650,ry+40,140,"Load",False,False,KEY_F4,"Load an existing map") Then 
			FileName$ = InputBox("Enter filename to load (without .map extension)",200,300,400,40,True,"maps")
			If Instr(filename,".map") Then Replace(filename,".map","") ; just in case
			load(filename) ; call common load.
			moveToStartPosition()		
			DebugLog "Loaded: " + FileName$
			statusMsg("Loaded: " + FileName$)
		End If
		If Button(650,ry+60,140,"Load Last",False,False,KEY_F5,"Load the last edited map") Then
			FileIn = ReadFile("prefs.txt")
				FileName$ = ReadString(FileIn)
			CloseFile(FileIn)
			If Len(FileName$) > 0 Then Load(FileName$):	moveToStartPosition()
		End If
End Function

Function moveToStartPosition()

	; find a start position and move the screen there
	For i=0 To 254
		For j=0 To 254
			tile=map(i,j,LayerObject)
			If tile>0
				For k=0 To 5
					script$=Lower(objectdef(tile)\script[k])										
					If Instr(script,"positionplayer")						
						; we've found it...
						sc_x=((i-10)*32)
						sc_y=((j-10)*32)
						Return
					EndIf
				Next
			EndIf
		Next
	Next
End Function

Next follow the instructions below to amend map.bb
;*****map.bb, in Load(), replace object load with:
	NumDefaultObjects=LoadObjectFile("maps\default")
	NumLevelObjects=LoadObjectFile(FileName$,MAX_DEFAULT_OBJECTS)

;*****map.bb, replace LoadObjectFile, add SaveObjectFile functions
  Function LoadObjectFile(FileName$,startid=1)
  	;load the object file
  	filename=Replace(filename,".map","")
  	
  	If startid=1 Then Delete Each objdef
  		
  	If FileType(FileName$+".obj") = 1
  		filein = ReadFile(FileName$+".obj")
  		num=parse(ReadLine(filein),"=",1)		
  		ReadLine(filein) ; blank space
  		If num>0 Then
  			;Dim objectdef.objdef(startid+num)
  			If startid+num>MAX_OBJECTS Then num=MAX_OBJECTS-startid:DebugLog "Tried too load too many objects!  Trimmed object list to "+MAX_OBJECTS			
  			For i=startid To startid+num
  				objectdef(i)=New objdef
  				objectdef(i)\image=Trim(parse(ReadLine(filein),"=",1))				
  				objectdef(i)\trigger=Trim(parse(ReadLine(filein),"=",1))			
  				;add a separator to the start to make searching simpler
  				If Left(objectdef(i)\trigger,1)<>"|" Then objectdef(i)\trigger="|"+objectdef(i)\trigger
  				objectdef(i)\name=Trim(parse(ReadLine(filein),"=",1))			
  				
  				For n=1 To CountString(objectdef(i)\trigger,"|")
  					objectdef(i)\script[n]=ReadLine(filein)
  				Next
  				ReadLine(filein); blank space
  			Next
  		End If
  		DebugLog num+" objects loaded from "+FileName+".obj"
  		CloseFile filein
  	Else
  		DebugLog "Filename does not exist: " + FileName$+".obj"
  	EndIf	
  	Return num
  End Function

  Function SaveObjectFile(filename$,startid,num)
  	; save the object definition file
  	fileout = WriteFile(FileName+".obj")
  	WriteLine fileout,"total objects = "+num ; number of objects
  	WriteLine fileout, "" ; blank line
  	For i=startid To startid+num-1
  		WriteLine fileout, "image = "+objectdef(i)\image
  		WriteLine fileout, "trigger = "+Mid(objectdef(i)\trigger,2) ; chop off the first | for saving
  		WriteLine fileout, "description = "+objectdef(i)\name		
  		For n=1 To CountString(objectdef(i)\trigger,"|")
  			WriteLine fileout,objectdef(i)\script[n] 
  		Next
  		WriteLine fileout,"" ; blank line
  	Next
  	CloseFile fileout
  End Function  

;*****map.bb, in Save(), remove object load code and add
	saveObjectFile("maps\default",1,NumDefaultObjects)
	SaveObjectFile(filename$,MAX_DEFAULT_OBJECTS,NumLevelObjects)

;*****map.bb, replace drawmap()
  Function drawmap(xpos,ypos,layer,ineditor=False)
  ; This function draws the map
  ; NB: for all "if ___ then Draw ___" lines, multiply the expression by map(x+xoff,y+yoff,LAYER_VISIBLE)
  
  ; determine sizes
  blocksize=32
  If ineditor Then
  	max_x=19
  	max_y=14
  Else
  	max_x=20
  	max_y=16
  EndIf
  	
  ; sort out the block location based on pixel co-ord
  xoff = Floor(xpos/blocksize)
  yoff = Floor(ypos/blocksize)
  
  ; find the pixel offset
  xpxoff = (xoff * blocksize) - xpos
  ypxoff = (yoff * blocksize) - ypos
  
  
  ; draw base layer
  ; uses draw block so you don't need a cls and is quicker.
  If layer = LayerBase
  	For x = -1 To max_x
  		For y = -1 To max_y
  			If x+xoff>=0 And y+yoff>=0 And x+xoff<=255 And y+yoff<=255		
  				If map(x+xoff,y+yoff,layer)>0 Then
  					If map(x+xoff,y+yoff,LAYER_VISIBLE)>0 Or ineditor Then DrawBlock btiles,(x*blocksize)+xpxoff,(y*blocksize)+ypxoff,map(x+xoff,y+yoff,layer)-1						
  				EndIf
  			EndIf
  		Next
  	Next
  EndIf
  
  If layer = LayerObject
  	For x = -1 To max_x
  		For y = -1 To max_y
  			If x+xoff>=0 And y+yoff>=0 And x+xoff<=255 And y+yoff<=255
  				tile=map(x+xoff,y+yoff,layer)
  				If tile>0 Then
  					If ineditor Then 
  						img=Abs(objectdef(tile)\image)
  					Else 
  						If map(x+xoff,y+yoff,LAYER_VISIBLE)>0 Then img=objectdef(tile)\image Else img=-1
  					End If
  					If img>=0 Then DrawImage objects,(x*blocksize)+xpxoff,(y*blocksize)+ypxoff,img
  ; following is useful for debugging objects in the editor
  ;					If ineditor Then Color 255,255,0:Rect (x*32)+7,(y*32)+7,18,18,False:Text (x*32)+40,(y*32)+8,tile,True
  				EndIf
  			EndIf
  		Next
  	Next
  EndIf
  
  ; draw other layers
  If layer = LayerTop And use_overlay_layer
  	For x = -1 To max_x
  		For y = -1 To max_y
  			If x+xoff>=0 And y+yoff>=0 And x+xoff<=255 And y+yoff<=255			
  				If map(x+xoff,y+yoff,layer)>0 Then 
  					If map(x+xoff,y+yoff,LAYER_VISIBLE)>0 Or ineditor Then DrawImage dtiles,(x*blocksize)+xpxoff,(y*blocksize)+ypxoff,map(x+xoff,y+yoff,layer)-1		
  				End If
  			EndIf
  		Next
  	Next
  EndIf
  
  ; layer 4, block overlay no transparancy
  If layer = LayerBlock
  	For x = -1 To max_x
  		For y = -1 To max_y
  			If x+xoff>=0 And y+yoff>=0 And x+xoff<=255 And y+yoff<=255			
  				If map(x+xoff,y+yoff,layer)>0 Then 
  					If map(x+xoff,y+yoff,LAYER_VISIBLE)>0 Or ineditor Then DrawBlock btiles,(x*blocksize)+xpxoff,(y*blocksize)+ypxoff,map(x+xoff,y+yoff,layer)-1			
  				End If
  			EndIf
  		Next
  	Next
  EndIf
  
  ; detail overlay
  If layer = LayerDetail
  	For x = -1 To max_x
  		For y = -1 To max_y
  			If x+xoff>=0 And y+yoff>=0 And x+xoff<=255 And y+yoff<=255		
  				If map(x+xoff,y+yoff,layer)>0
  					If map(x+xoff,y+yoff,LAYER_VISIBLE)>0 Or ineditor Then DrawImage dtiles,(x*blocksize)+xpxoff,(y*blocksize)+ypxoff,map(x+xoff,y+yoff,layer)-1						
  				EndIf
  			EndIf
  		Next
  	Next
  EndIf
  
  
  If layer = LayerHits And ineditor
  	Color 255,255,255
  	For x = -1 To max_x
  		For y = -1 To max_y
  			If x+xoff>=0 And y+yoff>=0 And x+xoff<=255 And y+yoff<=255	
  				If map(x+xoff,y+yoff,layer)=-1 Then Text (x*32)+8,(y*32)+8,map(x+xoff,y+yoff,layer),True
  				If map(x+xoff,y+yoff,layer)>0 Then Text (x*32)+8,(y*32)+8,map(x+xoff,y+yoff,layer),True
  			End If
  		Next
  	Next
  EndIf
  
  If layer=LayerCollision And ineditor
  	For x=-1 To max_x
  		For y=-1 To max_y
  			If x+xoff>=0 And y+yoff>=0 And x+xoff<=255 And y+yoff<=255	
  				Select map(x+xoff,y+yoff,LayerCollision ) 
  				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
  			End If
  		Next
  	Next
  End If
  
  End Function

;*****map.bb, add
   Global use_overlay_layer=true

I know, yuck. sorry, it ended up being more intrusive than I first thought. Next we have a whole host of little changes, as follows:
;*****consts.bb, add these
   Const MAX_DEFAULT_OBJECTS = 100
   Const MAX_OBJECTS		  =255

;*****consts.bb, change this dim statement
   Dim objectdef.objdef(MAX_OBJECTS)

;*****consts.bb, remove this
   Const NumObjects

;*****initialise.bb, replace in startgame()
	loading((numDefaultObjects+NumLevelObjects)+" objects loaded.")

;*****editor include.bb, add to crreaterandommap
	NumDefaultObjects=LoadObjectFile("maps\default")

;*****editor include.bb, remove drawmap_ineditor, remove LoadLast
;*****engine.bb, add Cls to top of draw(), remove global use_overlay_layer

And that should be it. Be careful, as these are some quite intrusive changes - hopefully I got everything.
Looks like once this lot are in, we have a new thread on our hands...

Rims, I cannot get this working at all, errors all over the place. I assume you're running the latest version from the site? (24-10-2004)

I don't want to even attempt to get Marks stuff in until I've got your stuff working.

Any chance of uploading your code?

These detailed instuctions of slotting bits in is just getting stupid, it's near impossible to follow. I can't work magic here.

Can't you guys just upload new versions? I can still host them off my site, so you'll only need to let me download them.

Here's my plan:
1. Rims, upload your code.
2. Mark, download Rims codes, then upload your code.
3. Rims delete your the code off your site.
4. I'll download Marks code and put my changes in and upload the whole lot to the site and start a new thread.
5. Mark delete your the code off your site.

I downloaded your code and placed it in a completely new directory. I then copied the inventory.bb above and overwrote the old one.

I get four errors:

error: "duplicate identifier" in inventory.bb
solution: delete pickedup Type in util.bb

error: "type invitem not found" in player.bb
solution: change Field inv.invItem[20] to Field inv.pickedup[20]

error: "illegal type conversion" in script.bb
solution: change "Pv\player = pl\id" to "Pv\player = pl"

error: "illegal type conversion" in players.bb (line 343)
solution: change:
If p\player = player And p\obj =keyid Then Delete p:Return True

to
If p\player\id = player And p\obj =keyid Then Delete p:Return True


and it works. Stick
drawBothInventories()
in the draw() function and
If KeyHit(57) Then showInventory(First player)
somewhere to open the inventory window. It's basic, but it works for the moment.To get it to work properly you just have to add an object to the inventory. This happens when in the script.bb when you pick stuff up. In the script.bb file on line 263 ("case addkey") change the code from:
Pv.pickedup = New pickedup
Pv\player = pl\id
Pv\obj=Int(si\param)+2

to
; (obj, weight, player.player, image, frame)
InvItem_Create(Int(si\param)+2,0,pl,objects,Int(si\param)+1)

And that's it. I tried to keep it all contained in the inventory.bb file, that's why it's a little complicated. I'm trying to make it as modular as I can.

I'll look at this tomorrow. I too have limited time (believe it or not).

Oh, and to get rid of the other inventory updates that are no longer needed:

remove players.bb->line 169 (this used to open the inventory)
and remove code in players.bb->Player_Create.player(Name$,X%,Y%,id%) (line 95)
	For i=1 To 5
		InvItem_Create("A_"+i,"50",First player,0,0)		
	Next


I should have a couple of hours this evening spare to put this all back together. I did think about posting a wholesale copy of map.bb (leaving just the one / two liners in other files). But I wasn't sure if either you or Rims had made any changes there recently, and decided it might just serve to confuse!

Let me know by 1900 GMT if you need me to do stuff, and I'll get on it!

Right, can't get any of this working... Can you please upload code or forget the whole phucking thing quite frankly. It's harder to detail these instructions than upload code surely?

I've just spent the last hour poncing around with this.

RIMS upload your code first.

MARK, After Rims has uploaded can you please update it and upload your stuff.

It's pretty straight forward. I ran through it four times myself... but either way here's the code.zip.

www.3030deathwar.co.uk/downloads/ab/code.zip

I downloaded the code from your site yesterday. I updated it. I uploaded it. Here it is. This is the code you want. This is the updated version. It's here. This is the one you want. if it doesn't work, it doesn't work.

The game runs... Although I don't appear to be able to pick anything up?

Rim's It's pretty straight forward because you wrote it. The changes you made from your first inventory system went in, then you updated it... No idea if the old stuff was supposed to come out or stay in. Uploading the code seemed to me to be the easiest option. As I said the code runs now... although picking things up doesn't seem to happen.

Mark, you want to download this and upload your changes?

After Marks made the changes for the editor stuff and converted the map I'll look into it all again. I've got a bunch of changes I've made but I've been waiting to get everyone elses stuff in first so I can upload a new version with all the changes rather than upload my changes then you guys have to re-arrange all your code to fit round the new mods I'd made.

So... After Mark comes back with the new version with new editor, map and that lot then I'll put my bits and pieces in, upload a new version and we'll all be happy again.

I have 2 AB folder on my computer, alienbreed (this is the uploaded version and the one I update with everyones changes) and alienbreed.dev this is the one I make my changes on, this way I can move my stuff over to the alienbreed folder after everyone has made their changes. The downside of this approach is that you have to put your changes in last, it's been working so far with the updates. It's just these 500 page manuals for changing 10k of code seems a little unnecessasy.

Sorry matey. I tried to modify it as little as possible. If you download the code again you'll be able to pick up keys. Space will open/close the inventory.

You have to call InvItem_Create(obj,weight,p.player,image,frame) when you want to pick up an object. It's all in this version, so you can only pick up keys.

Did you ever get a response from SourceForge? A code version control system might help us here...

Also, I intend to do some digging for a good, free, visual difference highlighting program : I think we'll find it useful!

As for the reams of code, I keep track of changes in files that I don't think I'm hitting hard. My offender up there is map.bb - I just ended up hitting it harder than expected. Although it might have been simpler to post it wholesale, I knew that ran the risk of losing other peeps changes.

Anyway, I'm slotting my changes in now...hopefully be back in 10 mins...

Okay, will take a little longer - don't have the latest gfx, which'll take a bit longer to d/l over dialup...

This file comparison looks (from screenshots / description at least) like it may be useful.

*clicky*

If it's any good, we may be able to simply post all updated files as is, and Rob (or whoever's merging) can use this tool to make their life easier. Starting download of it for myself now - gfx.zip just hit 97%...

Nice one Mark, I've got a whole load of new gfx to add on the next upload, also I've added some stuff to the players and aliens classes and bb files.

Had no word from SourceForge.

Okay, here you go. It all appears to be working - I guess in this little whirlwind of development we may have knocked something out of kilter, but it all looks about right to me!

*download latest code here*

I actually used the tool above to do most of the merge - very neat! It did get a bit confused in map.bb, but I tracked it down to a dodgy invisible char at the end of a line, so fair enough! Of course, I then forgot that I needed to update room files, add a tile, needed to add your switch tile to the test.obj... well anyway, it's done now!

Someone mentioned an alternate CVS based free site on an old thread, I may scan back to see if that's any good.

Yeah! Everything appears to be working... Time for bed now! I'll take a look at this over the next couple of days... get my changes in and re-upload!

Right Mark, level objectives... I don't think they should be player specific, I think it should be level specific, I don't think giveobjectivetoplayer is right, it'll get confusing with 2 player modes.

Once the level objectives are in and a couple of tweaks here and there I think we'll be about ready to start level designing and actually having a game!!

Once I've got my head round what needs to be done I'll sort out the todo list.

Nighty Night everyone!

Yeah, not sure about objectives for individual players - we can force 2 player puzzles by other means. So...my thinking is as follows:

Objectives can be mandatory (1 or more) or optional (none or more), and are defined per level. They are always shared by both players. However it might be neat if we remember WHO completes each). Objectives can also be added AND removed mid-level, as either mandatory or optional. (The latter two are nice to haves, we just need the basics for now).

This'll be next thing after sorting special object placement in the editor - that should be sorted tomorrow...

Also, I noticed a few 'features', but didn't have time to delve:
- In intex (and main menu screens), you should be able to use both the action key and fire key to do stuff - it's a bit inconsistent at the moment.
- My machine seriously slows down if many critters are active. While that's okay (i'm mostly in debug, and my pc is piss poor), it did appear that things weren't quite right. I thinking something may up in the timing, etc., although I didn't really have time to investigate further.
-grenades should drop to the floor when they hit something, not continue rising / falling!

- I've sorted the intex in my fixes, as well as a few other keyboardy things.
- My PC is really slow in debug too (Athlon 2600), smooth as silk when debug is off though. I think there's a lot of AI processing that gets hit by debug mode hard. I don't think there's a lot I can do about this... I'll take a look, but I don't really see it as a problem.
- The grenade thing, yeah, It's a new thing... I'll look into it.

I also want to implement fire button 2 more, Ie, Have Machine gun on fire 1 then Grenade launcher on fire 2. This does mean less weapons, but more options. Which I don't think would be a bad thing as I think six weapons is too many anyway, there are enough keys as it is.

Thoughts of other weapons:
A Timed bomb, very much like the grenade but you drop it at your feet.
More bouncing weapons! I like shooting round corners!

Also, I like the idea of shooting an alien with a flame thrower it it catching on fire!

Anyway... Nearly 9:00am... Should be working!

The slowdown thing was more that it seemed to be exhibiting some weird behaviour (like forgetting to draw aliens?!?).

In one instance I entered a room with about 15 aliens and things start to *REALLY* slow down to like 1 update per second. While the slowdown itself is okay, the logic ought to be able to catch up. The aliens headed straight for me and surrounded me, I fired a grenade, I heard it blow up, but no aliens seemed to die. And even before I fired the grenade, on one render, the aliens had vanished! Once all aliens had been killed, the game then took about 5 seconds to get back to normal. So it is likely to be the aliens in some way...

At some point I'll take a look myself, I'm just saying that I suspect that something in the frame limiting / speed / logic / drawing isn't quite getting called / slowed / sped up right.

Definitely like the flamethrower. Guided missiles might be cool too...

What do you think of the alternate fire options (fire button 2)?

I'm also going to add different ammo for different weapons, so even if you have the machine gun if you don't have grenade ammo you won't be able to launch grenades, so really it's very much like the current situation, just with less fiddling with different guns the whole time.

Definitely agree on sorting out different ammo types - I was never entirely convinced by the "one ammo fits all" idea. However, I reckon you should attempt to make it such that certain gun types can share ammo. This way we always have the option to reverting to a "one ammo fits all" approach if wanted on a given game module...

Different fire actions - in principle, I like the idea. However, if we do this, I reckon it needs to be tied to the weapon (singular) currently held. i.e. a given weapon might be able to fire bullets and launch grenades. Obviously that makes things more complicated than simply assigning two separate weapons to each button.

I also still like the idea of restricting how many weapons you can carry. Maybe we do this through the weight in the inventory system - make most weapons dead heavy, and keys dead light (0 weight). This will set up some neat scenarios in game too, as you could have a section where you need to move a heavy object from one room to another, forcing you to drop all bar one weapon...

I've thought about dropping stuff, and this could cause a bit of a problem as you can't drop 2 things on the same tile. So you'll have to drop things around a room for example. Also if you drop a gun, then the script (as it stands) gives you a full ammo clip with the gun, so if you're running out of ammo you'd be able to drop it and pick it up again and you'll be reloaded. Likewise you've got the problem with clips too, drop a clip, pick up a pick and the clip will be reloaded again.

I'm sure there'll be some clever scripting thing to sort this but it'll certainly require a lot of thought.

Well, for now let's just stick with picking anything and everything up, and assign two fire keys to two separate weapons. Assign all objects as zero weight and log the other ideas as nice to haves...

Ah, the free CVS site was www.cvsdude.org.

It looks pretty good, have requested an account which should be sorted in about 20 mins apparently! Will have a play around when I get back from the vets (stressed cat - again!). Terms seem very simple, check it out, see if yoyu like it!

In terms of client software, I have TortoiseCVS installed which completely integrates the versioning stuff with windows explorer - just right click to check stuff in / out. Haven't trie it, but it looks cool. There are others listed on cvsdude too...

Found the 'catch'.

Free service is 2MB only, and just one 'admin' user.

That's probably okay for us, provided we stick the media elsewhere - thoughts? There may also be a limit on transfer amounts, as upgraded accounts say it's unlimited, but there's no mention of it on the free one. I guess it may be that abusers of the free space get kicked?

(There's an upgraded 'Home' option for $15 for 3 months at 20MB and three 'admins', and unlimited transfers...)

I can hold the Media on my NTL site, there doesn't appear to be a limit on bandwidth or anything so that's no biggy.

Anyway... I'll get a new version sorted over the next couple of days upload it, start a new thread and then we'll look into using a CVS!

Continued Here: http://www.blitzbasic.com/Community/posts.php?topic=39934