; time for some serious coding of AI
; fixed register allocations :
; d3 = current car being checked
; a1 = cars 3d address [the car being tested, not necessarily the players car]
; a2 = cars ai block address

.include 'ai.inc'

.text
AI::	movem.l	d0-d5/a0-a3,-(sp)
;	tst.b	game_over.w
;	bne	.ex
;	tst.b	weather.w
;	beq.b	.sun

;	move.w	#140,max_skill.w
;	move.w	#50,min_skill.w
;	move.w	#230,boost_max.w
;	bra.b	.in

.sun:	move.w	#160,max_skill.w		; this is max for WORST car
	move.w	#60,min_skill.w
	move.w	#256,boost_max.w

.in:	move.l	race_timer.w,time_stopped.w
	moveq	#0,d3				; BIG bad BUG - didn't make sure loop counter was properly initialized !!!!!
	move.b	d3,active_count.w		; count drones in race mode - if 0 then all have finished, so game is over (you've lost!)
	move.b	real_num_drones.w,d3		; counter for number of data blocks to process
	bsr	positions		; updates list of positions of cars, in lap/track segment terms

.loop:	bsr	prepare_pointers	; point at the ai block and the 3d data
	move.w	#top_speed,target_speed.w
	moveq	#$F,d0			; status mask - status is low nibble, flags in high nibble
	and.b	STATS(a2),d0		; read this cars status
	cmpi.b	#human,d0		; in the case of player controlled cars, their position data needs to be available
	bne.b	.nh			; in the same format as the drones, including segment etc.
	bsr	convert_type		; routine to read player car data and write back as if drone car
	bra.b	.dl
.nh:	cmpi.b	#out_of_it,d0		; make sure cars are already running, and should be under ai control
	beq.b	.dl			; if not then loop round - don't waste time doing extra work
	cmpi.b	#parked,d0
	beq.b	.dl
;	cmpi.b	#won_it,d0		; temporary lockup of all cars crossing finish line
;	beq.b	.dl
;	cmpi.b	#finished,d0
;	beq.b	.dl

	cmpi.b	#racing,d0
	beq.b	.std
;	asr.w	target_speed.w		; make finished cars do one slow lap - actually want to park, so needs more thought
	move.w	#50,target_speed.w		; make cars try to end up parked at the right place
	bra.b	.sk

.std:	addq.b	#1,active_count.w	; count car because its active
.sk:	bsr	where_to_go		; slow routine - needs to be faster
	bsr	how_fast		; fast in comparison
	bsr	apply_movement		; not only moves car but updates lap and segment codes as well - very slow !
.dl:	add.w	#16,max_skill.w
	add.w	#16,min_skill.w
	dbra	d3,.loop

.ex:	tst.b	active_count.w
	bne.b	.not
	cmp.b	#1,race_type.w
	beq.b	.not
	st.b	game_over.w		; if no cars active, game is done, but don't clear it ever
.not:	movem.l	(sp)+,d0-d5/a0-a3
	rts

where_to_go::
	bsr	get_track_target	; if necessary goes through track segments looking for a new one to aim at
	bsr	make_into_angle		; turn an x,z target into a 10 bit signed angle
	bsr	test_local		; check for local blockages and adjust target if anything to avoid - must be made faster !!!!!
	rts

how_fast::
	bsr	check_turn		; adjust speed if corner coming up, and set steering amount / change
	bsr	check_position		; make sure balance between winning and losing is maintained
	rts

apply_movement::
	movem.l	d0-d3,-(sp)
	bsr	calculate_steps 	; work out distance to target, and divide into steps based on knowledge of speed
	bsr	calculate_steer		; work out steering amount required to accomplish the required turn in that many steps

	move.w	V_DIR(a2),d0
	bsr	get_sine
	neg.w	d0
	move.w	d0,6(a1)		; set car to point in right direction [ this refers to drones, not humans]

	move.w	VELOC(a2),d0
	muls	framect.w,d0	; small number hence less likely to overflow 16 bit than next one
	muls	SKILL(a2),d0
	asr.l	#4+2,d0		; extra two, so that old frame update speed assumed at 4 fps

	bsr	test_for_tight
	beq.b	.not
	move.w	d0,d3
	asr.w	#2,d3
	sub.w	d3,d0
	asr.w	#1,d3
	sub.w	d3,d0

.not:	cmp.w	#$5c00,d0
	blt.b	.ok
	move.w	#$5c00,d0

.ok:	muls	speed_adjustment.w,d0
	asr.l	#4,d0

	muls	d0,d1
	muls	d0,d2
	asl.l	#2,d1
	asl.l	#2,d2
	swap	d1
	swap	d2
	ext.l	d1
	ext.l	d2

	add.l	d1,12(a1)
	add.l	d2,20(a1)

	bsr	convert_type		; puts modified segment and lap count into ai block

	movem.l	(sp)+,d0-d3
	rts



prepare_pointers:			; points a1 at the cars 3d data and a2 at its ai block
	move.l	d0,-(sp)
	movea.l	carsad.w,a1		; read address that points to all the cars
	movea.w	#ai_blocks,a2		; address of ai information blocks
	move.w	d3,d0			; copy car number
	asl.w	#5,d0			; scale up
	adda.w	d0,a1			; adjust address of car
	adda.w	d0,a2			; and also info block
	move.l	(sp)+,d0
	rts

get_track_target:
	moveq	#$f,d0
	and.b	STATS(a2),d0
	cmp.b	#racing,d0		; doesn't do anything unless car is racing
	bne.b	.ex
;	beq.b	.ok
;	move.b	d3,d0
;	bsr	change_target

.ok:	clr.b	brake_request.w		; make sure this is ready in case brakes are required
	bsr	get_distance		; returns distance in d0
	bsr	test_for_tight		; test for tight turn flag
	beq.b	.oky
	cmpi.l	#10000,d0		; use different min distance for tight turns
	bgt.b	.ex
.oky:	cmpi.l	#min_distance,d0
	bgt.b	.ex			; if greater then no change is required
	bsr	next_segment		; special routine which will get more complex when pits are added
	bsr	change_target		; put new target x and z and y' in place
	bra	.ok			; go round again : just in case movement is very fast
.ex:	rts

test_for_tight:
	movem.l	d0-d1/a1,-(sp)
	movea.l	#track_table,a1
	move.w	SEGS(a2),d0		; get current segment number
	asl.w	#3,d0			; scale up !!!
	move.b	7(a1,d0.w),d1		; read status
	btst	#2,d1			; check for tight turn
	movem.l	(sp)+,d0-d1/a1		; returns eq if not tight, ne if tight
	rts

get_distance:
	move.l	12(a1),d0		; get x
	sub.l	TGT_X(a2),d0
	bpl.b	.nnx
	neg.l	d0
.nnx:	move.l	20(a1),d1		; get z
	sub.l	TGT_Z(a2),d1
	bpl.b	.nnz
	neg.l	d1
.nnz:	cmp.l	d0,d1
	blt.b	.nxg
	exg.l	d0,d1
.nxg:	asr.l	#1,d1
	add.l	d1,d0			; total distance
	move.l	d0,target_distance.w
	rts

next_segment:
	move.w	TGT_S(a2),d0		; get current target segment number
	movea.l	Genstr+20+(8*str_track),a0		; address of track segment list
	move.l	(a0)+,d1
	addq.w	#1,d0			; segment number + 1
	cmp.w	d1,d0			; test against limit - note that later this test will be much harder
	blt.b	.ok			; if new segment number is less than limit then ok
	moveq	#0,d0			; otherwise we now have 0 segment
.ok:	rts

change_target:
	move.w	d0,TGT_S(a2)
	asl.w	#5,d0
	adda.w	d0,a0			; point at 3d data for track segment
	move.l	12(a0),TGT_X(a2)
	move.l	20(a0),TGT_Z(a2)
	rts	

make_into_angle:
	movem.l	d3/d4,-(sp)
	move.l	TGT_X(a2),d3
	sub.l	12(a1),d3		; get dx
	move.l	TGT_Z(a2),d4
	sub.l	20(a1),d4		; get dz
	bsr	get_atan
	movem.l	(sp)+,d3/d4
	
	move.w	d0,TGT_Y(a2)		; put angle into target angle, since we are not working on track angles any more.

	rts

convert_type:				; needs to put the lap and segment codes in place for this car
	move.w	SEGS(a2),current_segment.w	; special for quick searches
	bsr	faster_find_segment	; gets segment code for this car

	moveq	#$f,d2
	and.b	STATS(a2),d2		; read current status

	cmp.b	#human,d2
	bne.b	.nsp

	move.l	speed.w,d0
	asr.l	#4,d0
	move.w	d0,VELOC(a2)

	move.w	6(a1),d0
	neg.w	d0
	move.w	d0,V_DIR(a2)		; an idea to try to make the human controlled car appear as a drone to the others

.nsp:	move.l	Dronec.w,d0		; get new segment code
	beq.b	.lap			; if code is zero then have finished a lap

	cmp.w	final_segment.w,d0
	bne.b	.nobk			; if not on last one, then no need to check for backwards driver
	move.w	SEGS(a2),d1
	bne.b	.nobk			; if last one wasn't zero, then not backwards
	cmp.b	#human,d2		; only humans matter because drones don't try to cheat (yet)
	bne.b	.nobk
	clr.b	dont_lap.w
	subq.b	#1,LAPS(a2)		; so that position doesn't leap ahead

.nobk:	cmpi.b	#won_it,d2
	beq.b	.last
	cmpi.b	#finished,d2
	bne	.nl			; if not on last (ie after finish) lap then just carry on

.last:	bsr	get_distance		; wrrecks d1, returns d0=distance to target
	cmp.l	#2000,d0		; test for car being on its parking space
	bgt	.nl
	move.b	#out_of_it,STATS(a2)
	bra	.nl

.lap:	move.w	SEGS(a2),d1		; get current segment code
	beq	.nl			; if already there then drop off because lap was completed previous go
	cmp.w	final_segment.w,d1	; test against highest segment number
	bne	.nl

	move.l	time_stopped.w,d1		; read current total
	cmp.b	#human,d2
	beq.b	.hum			; human player has own section

	addq.b	#1,LAPS(a2)		; for others, just check that 
	beq	.nl			; we ain't crossing into lap 0 
	bra.b	.nh			; in which case it don't count

.hum:	tas	dont_lap.w		; for the human we must check for previous reverses
	bne.b	.norml			; if not then don't use this bit
	addq.b	#1,LAPS(a2)		; which increments lap as usual
	bra	.nl			; but then ignores it
.norml:	addq.b	#1,LAPS(a2)		; whereas the normal version only ignores it if its into lap 0
	beq	.nl

	move.l	d1,lap_start.w		; lap time change happens so long as its human
	move.b	LAPS(a2),lap_display+1.w
.nh:	move.w	LTIME(a2),d0		; read start time of this lap
	move.w	d1,LTIME(a2)		; put new start time of lap in place
	sub.w	d0,d1			; calculate full lap time
	move.w	LBEST(a2),d0		; get current bestest time
	beq.b	.wb
	cmp.w	d0,d1
	bpl.b	.nw
.wb:	move.w	d1,LBEST(a2)
	cmp.b	#human,d2
	bne.b	.nw
	bsr	Timer_down		; this is two fold - it moves the main timer down, and copies it into the 'fastest' timer
.nw:	move.b	LAPS(a2),d0
	cmp.b	qty_laps.w,d0		; see if race is over
	bne	.nl
	move.l	time_stopped.w,FTIME(a2)
	movea.w	finish_address.w,a0	; record car crossing line
	move.b	d3,(a0)			; in the appropriate place
	addq.w	#1,finish_address.w	; and move that place on for next car
	movea.l	park_ad.w,a0
	move.l	-(a0),TGT_Z(a2)
	move.l	-(a0),TGT_X(a2)
	move.l	a0,park_ad.w
	moveq	#won_it,d2
	tst.b	race_won.w
	bne.b	.ntw
	move.b	d3,winner.w
	bra.b	.ws
.ntw:	addq.b	#finished-won_it,d2
.ws:	moveq	#$f,d0
	and.b	STATS(a2),d0
	cmp.b	#human,d0
	beq.b	.skq
	move.b	d2,STATS(a2)
	move.w	#128,VELOC(a2)		; cuts speed by 50% instantly
.skq:	addq.b	#1,race_won.w		; race_won now counts the cars across the line, and is still zero until race is won
	cmp.b	#human,d0
	bne.b	.nl

	moveq	#0,d0
	move.b	-1(a0),d0		; read preceding car across line (grid number, not colour)
	asl.w	#5,d0
	movea.w	#ai_blocks,a0
	move.l	FTIME(a0,d0.w),d1	; get preceding car's time
	cmp.l	FTIME(a2),d1		; check against this one
	bne.b	.okf
	movea.w	finish_address.w,a0
	move.b	-2(a0),d0
	move.b	-1(a0),-2(a0)
	move.b	d0,-1(a0)

.okf:	st.b	game_over.w
	st.b	human_finished.w	; this is very important to stop position changing
;	clr.w	revs.w
;	clr.l	speed.w

.nl:	move.w	Dronec+2.w,SEGS(a2)
	rts
	

test_local:
	moveq	#0,d2
	move.b	real_num_drones.w,d2		; loop counter
	clr.b	direction_flags.w

.loop:	cmp.w	d2,d3			; check if its self
	beq.b	.dl

	bsr	get_separation		; returns approx distance to other car in d1 and x / z distances in d3/d4
	cmpi.l	#max_distance,d1	; check within reasonable distance
	bgt.b	.dl
;	move.l	d1,d5			; keep distance in d5 for safekeeping

	bsr	check_ahead		; make sure relative direction is ahead rather than behind
	cmpi.w	#$f0,d0			; angles over $100 are behind, so limit to somewhat less for safety
	bgt.b	.dl			; in which case drop out

	bsr	check_speed		; make sure that other car is getting closer to you
	cmp.l	csep.w,d0
;	sge.b	hit_risk.w
	bge.b	.dl			; if new distance is greater than old distance ...

	cmpi.l	#crash_dist,d0		; see if new distance will result in a crash - perhaps should trigger sound effect ?
	bgt.b	.ok

;	move.l	d0,-(sp)
;	sfx	crash,$7fff,0,10
;	move.l	(sp)+,d0

	move.w	VELOC(a0),d0
	asr.w	#1,d0
	move.w	d0,VELOC(a2)		; make this car have half the speed of the one in front !

.ok:	bsr	set_blocks		; set bits which prevent movement in certain directions

.dl:	dbra	d2,.loop

	bsr	choose_direction	; based on limitations produced from above

	rts

get_separation:				; calculate separation from car d2 to car d3 [which is already pointed to by a1]

	movem.l	d3/d4,-(sp)
	movea.l	carsad.w,a0		; read address that points to all the cars
	move.w	d2,d0			; copy second car number
	asl.w	#5,d0			; scale up
	adda.w	d0,a0			; adjust address of car

	move.l	12(a0),d0
	sub.l	12(a1),d0
	move.l	d0,d3			; kept for atan
	move.l	d0,Xoffst.w		; retain for later
	bpl.b	.nnx
	neg.l	d0
.nnx:	move.l	20(a0),d1
	sub.l	20(a1),d1
	move.l	d1,d4			; kept for atan
	move.l	d1,Zoffst.w
	bpl.b	.nnz
	neg.l	d1
.nnz:	cmp.l	d1,d0
	bgt.b	.nxg
	exg.l	d0,d1
.nxg:	asr.l	#1,d1
	add.l	d0,d1			; separation approximation
	move.l	d1,csep.w

	cmpi.l	#max_distance,d1	; check within reasonable distance
	bgt.b	.dl			; remove longest part if we know its too far anyway

	bsr	get_atan		; calculate angle (in to d0)

.dl:	movem.l	(sp)+,d3/d4		; finally returns d0 and d1
	rts

check_ahead:				; checks that position of d2 wrt d3 is in the 'ahead' direction
	move.l	d0,d1
	sub.w	V_DIR(a2),d1		; angular difference
	asl.w	#6,d1
	asr.w	#6,d1			; limit to +-$200
	move.w	d1,d0
	bpl.b	.nnd
	neg.w	d0			; get abs angle
.nnd:	rts

check_speed:
	movea.w	#ai_blocks,a0
	move.w	d2,d0
	asl.w	#5,d0
	adda.w	d0,a0

	movem.l	d1-d4,-(sp)	; < < < < < this is what you're looking for !
	move.w	V_DIR(a2),d0
	bsr	get_sine
	muls	VELOC(a2),d1
	muls	VELOC(a2),d2

	move.l	Xoffst.w,d3
	move.l	Zoffst.w,d4
	add.l	d1,d3
	sub.l	d2,d4

	move.w	V_DIR(a0),d0
	bsr	get_sine
	muls	VELOC(a0),d1
	muls	VELOC(a0),d2

	sub.l	d1,d3		; giving new vector
	bpl.b	.ok1
	neg.l	d3
.ok1:	add.l	d2,d4
	bpl.b	.ok2
	neg.l	d4
.ok2:	cmp.l	d3,d4
	blt.b	.nxg
	exg	d3,d4
.nxg:	asr.l	#1,d4
	add.l	d4,d3
	move.l	d3,d0		; this value returned is the new separation, and so needs comparing to the old one
	movem.l	(sp)+,d1-d4

	cmp.l	csep.w,d0
	rts

set_blocks::				; takes input angle in d1 which is within +-$f0 where right is negative
	add.w	#$100,d1		; turns into range $10-$1f0, where $200 is most left direction
	lsr.w	#6,d1			; scales down to range 0-7, which is perfect
	bset	d1,direction_flags.w
	rts

choose_direction::			; given 8 flags and a specific target direction, choose a new target direction
;	tst.b	hit_risk.w
;	beq.b	.oky

;	bsr	Rand2
;	bpl.b	.tl
;	bra.b	.tr

.oky:	move.w	TGT_Y(a2),d0		; desired direction
	sub.w	V_DIR(a2),d0		; current direction to give required change in direction
	asl.w	#6,d0
	asr.w	#6,d0			; make signed 10 bit - just in case
	sub.w	#$100,d0		; makes -$100to$100 into -$200to0
	neg.w	d0			; then into 0to$200
	lsr.w	#6,d0			; giving relevant bit code (0-8)
	btst	d0,direction_flags.w
	beq.b	.ok

.tr:	subq.b	#1,d0			; try to right
	bmi.b	.tl
	btst	d0,direction_flags.w
	bne.b	.tl
	sub.w	#$30,TGT_Y(a2)		; change 'target' direction
	bra.b	.ok

.tl:	addq.b	#2,d0
	cmpi.b	#7,d0
	bgt.b	.brake
	btst	d0,direction_flags.w
	bne.b	.brake
	add.w	#$30,TGT_Y(a2)
	bra.b	.ok

.brake:	st	brake_request.w

.ok:	rts				; direction is now chosen, within a small margin of the ideal, and with brakes on if collision imminent


check_turn:
	moveq	#$f,d0
	and.b	STATS(a2),d0
	cmp.b	#racing,d0		; doesn't do anything unless car is racing
	bne.b	.ex

	move.w	TGT_Y(a2),d0		; desired direction
	sub.w	V_DIR(a2),d0		; current direction
	asl.w	#6,d0
	asr.w	#6,d0			; turn into signed 10 bit value
	beq.b	.ex
	bpl.b	.nna
	neg.w	d0			; absolute change
.nna:	cmpi.w	#$20,d0			; check against a fairly small angle
	blt.b	.vsmall
	cmpi.w	#$50,d0
	blt.b	.small
	cmpi.w	#$80,d0
	blt.b	.norm

	sub.w	#150,target_speed.w
.norm:	sub.w	#100,target_speed.w
.small:	sub.w	#100,target_speed.w
.vsmall:sub.w	#100,target_speed.w	; these were 150,150,150,100 and that was ok except for very tight bends
	cmp.w	#10,target_speed.w
	bgt.b	.ex
	move.w	#10,target_speed.w
.ex:	rts

positions:				; loop through all the cars, and sort into order
	movem.l	d0-d3/a0-a2,-(sp)
	moveq	#0,d0			; for zapping
	moveq	#0,d1
	move.b	real_num_drones.w,d1	; count through cars, writing into position data block wherever they belong
	move.w	d1,d3
	movea.w	#posit_block,a0

.lp:	move.l	d0,(a0)+
	dbra	d1,.lp

	subq	#4,a0			; point at final entry in block

.loop:	movea.w	#ai_blocks,a2
	move.w	d3,d0
	asl.w	#5,d0
	adda.w	d0,a2			; now pointing at the ai block for this car
	move.l	a0,a1			; copy for the loop

	move.w	d3,d0
	asl.w	#8,d0
	move.b	LAPS(a2),d0
	swap	d0
	move.w	SEGS(a2),d0		; thus making d0 the whole item ready to write out

.ilp:	tst.l	(a1)
	beq.b	.write

	move.b	1(a1),d1
	cmp.b	LAPS(a2),d1
	bgt.b	.doswp			; if one in table has already done more laps, its ahead, and should be further up the table
	blt.b	.skip			; if its done less laps, then new item belongs higher up table

	move.w	2(a1),d1		; if the lap count is the same, we check the segment count in the same way
	cmp.w	SEGS(a2),d1
	bgt.b	.doswp
	blt.b	.skip

; there really should be a detailed position analysis tool here, but left out for now

.doswp:	move.l	(a1),d1
	move.l	d0,(a1)
	move.l	d1,d0

.skip:	subq.l	#4,a1			; move up the list
	bra	.ilp			; and try the next item

.write:	move.l	d0,(a1)
	dbra	d3,.loop


	movem.l	(sp)+,d0-d3/a0-a2
	rts				; table completed


check_position::
	move.l	d4,-(sp)
	movea.w	#posit_block,a0
	clr.b	human_ahead.w
	moveq	#0,d2			; position counter

.lp:	move.b	(a0),d1
	cmp.b	d1,d3			; is it me ?
	beq.b	.found

	addq.w	#1,d2
	moveq	#0,d0
	move.b	d1,d0
	asl.w	#5,d0
	movea.w	#ai_blocks,a3
	adda.w	d0,a3

	moveq	#$f,d0
	and.b	STATS(a3),d0	
	cmp.b	#human,d0
	seq.b	human_ahead.w		; trickery - will be cleared if a non_human is between you, so that only the drone behind the human is effected

	addq	#4,a0
	bra	.lp

.found:	moveq	#3,d4
	move.b	4(a0),d0		; get car behinds number
	asl.w	#5,d0
	movea.w	#ai_blocks,a3
	adda.w	d0,a3
	moveq	#$f,d0
	and.b	STATS(a3),d0		; read next cars status (ie car behind) in case its human
	cmp.b	#human,d0
	bne.b	.noh

	moveq	#30,d4			; allow to get 30 segments ahead of a human !

.noh:	tst.w	d2
	bne.b	.nf
	move.b	1(a0),d0
	cmp.b	5(a0),d0
	bne.b	.jc			; if not on same lap then straight out

	move.w	2(a0),d0
	sub.w	6(a0),d0		; calculate lead in segments over car behind
	cmp.w	d4,d0			; check if more than (d4=limit) segment ahead
	blt.b	.jc			; if less than limit then just carry on
	moveq	#-4,d0			; otherwise, deduct one from the skill value
	bra.b	.nobo			; to help bring car down to general standard

.nf:	move.b	1(a0),d0		; read current laps
	cmp.b	-3(a0),d0		; check against man in front
	bne.b	.jc			; when lap count is different its easier to forget it until its the same again

	move.w	-2(a0),d0		; get segment number of man in front
	sub.w	2(a0),d0		; get number of segments we are trailing

;	tst.b	human_ahead.w
;	beq.b	.nobo			; if not human ahead then we reduce trail count, to give 

;	subq.w	#2,d0
;	bmi.b	.over

;	add.w	SKILL(a2),d0
;	cmp.w	boost_max.w,d0
;	ble.b	.ok2
;	move.w	boost_max.w,d0
;	bra.b	.ok2

.nobo:	subq.w	#2,d0			; other drones are allowed to stay two segments ahead without any boost being applied
					; in fact skill level is dropped if they get too close !

.over:	add.w	SKILL(a2),d0		; make into new 'skill' value
.lim:	cmp.w	max_skill.w,d0
	ble.b	.ok1
	move.w	max_skill.w,d0
.ok1:	cmp.w	min_skill.w,d0
	bge.b	.ok2
	move.w	min_skill.w,d0
.ok2:	move.w	d0,SKILL(a2)		; write clipped skill value
.out:	move.l	(sp)+,d4
	rts

.jc:	move.w	SKILL(a2),d0		; jc = just checking
	bra	.lim


calculate_steps:
	tst.b	brake_request.w
	bne.b	braking
	move.l	target_distance.w,d0	; distance intended to travel
	move.w	target_speed.w,d1
	move.w	d1,d2
	add.w	VELOC(a2),d1		; combine current speed and intended speed
	beq.b	.zs		; ZERO PROTECTION
	lsr.w	#1,d1			; average speed
	divu	d1,d0			; to give number of steps expected
	bne.b	.nz
	moveq	#1,d0
.nz:	cmp.w	#max_steps,d0
	blt.b	.ok0
.zs:	move.w	#max_steps,d0
.ok0:	sub.w	VELOC(a2),d2
	ext.l	d2
	; ZERO PROTECTION by default
	divs	d0,d2			; in case speed change required is -ve
	cmpi.w	#top_accel,d2
	blt.b	.ok
	move.w	#top_accel,d2
.ok:	muls	framect.w,d2
	asr.l	#2,d2
	add.w	VELOC(a2),d2		; apply acceleration
.nn:	cmp.w	#top_speed,d2
	blt.b	.ex
	move.w	#top_speed,d2
.ex:	tst.w	d2
	bpl.b	.ex2
	moveq	#10,d2
.ex2:	move.w	d2,VELOC(a2)
	rts

braking:move.l	target_distance.w,d0	; distance intended to travel
	moveq	#0,d2
	move.w	VELOC(a2),d1		; combine current speed and intended speed (stop)
	move.w	d1,d2			; get speed change required
	beq.b	.sk		; ZERO PROTECTION
	lsr.w	#1,d1			; average speed
	divu	d1,d0			; to give number of steps expected
.sk:	cmp.w	#40,d0
	blt.b	.ok0
	move.w	#40,d0
.ok0:	ext.l	d2
	tst.w	d0
	beq.b	.sk2		; ZERO PROTECTION
	divu	d0,d2
.sk2:	cmpi.w	#top_brake,d2
	blt.b	.ok
	move.w	#top_brake,d2
.ok:	neg.w	d2
	add.w	VELOC(a2),d2		; apply acceleration
	bpl.b	.ex
	moveq	#0,d2
.ex:	move.w	d2,VELOC(a2)
	rts

calculate_steer::
	tst.w	VELOC(a2)
	beq.b	.nc			; no blipping around if not moving
	tst.w	d0			; ZERO PROTECTION
	beq.b	.nc
	move.w	TGT_Y(a2),d1
	sub.w	V_DIR(a2),d1
	asl.w	#6,d1
	asr.w	#2+2,d1			; multiplying up
	ext.l	d1
	divs	d0,d1
	bmi.b	.mi
	cmpi.w	#max_steer,d1
	blt.b	.ok
	move.w	#max_steer,d1
.ok:	muls	framect.w,d1		; scale up to make f.r.i.
	add.w	d1,V_DIR(a2)
.nc:	rts

.mi:	cmpi.w	#-max_steer,d1
	bgt	.ok
	move.w	#-max_steer,d1
	bra	.ok

initialise_ai::
	moveq	#0,d0
	move.b	d0,race_won.w	; make sure results are fully cleared before new race starts
	move.b	d0,play_out.w	; make sure doesn't start by stopping !
	move.b	d0,game_over.w	; and make sure we don't stop the whole thing after 2.3 seconds !
	move.b	d0,static_view.w
	move.b	d0,human_finished.w
	move.b	d0,camera_lock_switch.w

	move.b	real_num_drones.w,d0
	move.w	#pos1_car,finish_address.w
	movea.w	#ai_blocks,a0
	movea.l	carsad.w,a1
	st.b	pre_race.w	; prevent player moving until race is started
	st.b	dont_lap.w	; prevent player getting a lap count for crossing the line at start

.loop:	move.l	12(a1),(a0)+	; x
	move.l	20(a1),(a0)+	; z
	move.w	6(a1),d2
	neg.w	d2
	move.w	d2,(a0)+	; y
	move.w	#1,(a0)+	; segment just ahead of all cars
	moveq	#0,d1
	move.l	d1,(a0)+	; vel
	move.w	d2,(a0)+	; direction
	move.w	#$100,(a0)+	; skill
	move.b	d1,(a0)+	; 0 stats
	move.w	d1,current_segment.w
	bsr	faster_find_segment
	move.l	Dronec.w,d2
	beq.b	.nml
	move.b	#-1,(a0)+	; if non zero, then must be behind start line, and so needs to start on lap -1
	bra.b	.ok
.nml:	move.b	d1,(a0)+	; if zero then zero for lap count as well
.ok:	move.w	d2,(a0)+	; laps
	move.l	d1,(a0)+
	move.l	d1,(a0)+
	lea	$20(a1),a1	; next car
	dbra	d0,.loop
	rts

start_race::
	moveq	#0,d0
	move.b	real_num_drones.w,d0
	movea.w	#ai_blocks,a0
	move.b	d0,d1
	sub.b	grid_position.w,d1	; gives the number that will mean looking at the human car

.loop:	cmp.b	d0,d1
	bne.b	.nml
	tst.b	demo_mode.w
	bne.b	.nml
	move.b	#human,STATS(a0)
	bra.b	.dl

.nml:	move.b	#racing,STATS(a0)
.dl:	lea	$20(a0),a0
	dbra	d0,.loop

	moveq	#0,d0
	move.l	d0,race_timer.w	; initialise master clock
	move.l	d0,lap_start.w
	move.b	d0,pre_race.w
	move.l	#park_target+48,park_ad.w	; point at first entry ready for when people finish
	rts

.bss
ai_blocks::
	ds.b	ai_block_size * num_cars	; reserve space for appropriate number of data blocks

	ds.l	1		; extra to ensure at least even alignment, and a little spare space


.end

given target x,z, and current x,z and current direction y'
calculate the steps of length 'speed', decreasing by 'brake' per step, 
that will pass through that point facing in the direction y"

difficult huh ?

cut down version is to say :

1. target speed is known, so we just take the distance to target, 
 and divide it by the average speed, which will tell us how fast we 
 need to brake and the number of steps involved - approx.

2. the total turn required is known, so it is reasonable to simply request 
 that the turn step is the total turn divided by the number of steps

since this process will repeat every frame, there is no need to worry about
cars driving off into the sunset, as they will check and recheck their positions
so frequently.

however all this is doing is making sure the car is facing the right way after a #
given number of steps not making sure its in the right place.

this will require much more thought

possible quick solution : if the standard solution above produces a average direction - that is current direction
plus target direction over two - then this can be compared to the actual required direction to hit the target
however, steering is not maintained in current data block, which is not going to help matters

if steering was maintained, then what ?

maximum steer is already defined, so perhaps the steer assigned could be the amount needed to pass the required
direction [to hit the target] after half the steps
