Shooting and probability
2012-07-07
This week I tried to make the enemies shoot according to a probability calculated from the distance of the player. Result is so and so, doesn't really look 'intelligent'.
Sketch for map
2012-07-07
I present a first try for a map. Since inspiration comes from Moonstone, I'd also like something to move on the map, perhaps a fleet from Mars, during the late stage of the game (as the dragon in Moonstone). The planets will stay still, to make life easier.
Some info about the places:
Some dialog
2012-07-01
This time I've been working on plot, setting, characters and dialog. This is some sketches for dialog between two main characters, Evelaine and Justin:
- Killing, she says while swallowing a handful of pills. That's what my life is all about. Kill everything, everything that moves. Men, women, children alike. Robots, stones, or just debris. BANG! The young man aside of her nods nervously. - Why do you think I take these pills? Because of space? No. It's the space within, the vast emptiness of my soul. - How poetic. - Fuckin' a. Oh look, here's some of them. Hold on, boy! [First level] - Yeah, that's the last one, she says. No big deal, huh? - Sure. No big deal... - Hey, don't look so chocked. You see over there? That's Hangar. Yep. Never thought you would see it, did you? Well, there she is. Incredible how that peice of junk holds together, still. - They're attacking! Run for your life! screems a man as they enter the space station. - Run where? Who is attacking? says Evelaine. - Mars! They've envied us long enough, now they're back to get what they wanted! - The man is clearly overreacting, says Evelaine as he's running out from the room. A great boom echoes through the walls, and the floor trembles. - Or maybe not... Come, let's leave. - But my mission? says Justin. - It's your mission or your life, come on! She pushes him back into the space craft. As they fly away, a voice on radio is saying: ”Yes, Berto, I think that is quite correct: Hangar is now officially at war with the Mars Federation. Or perhaps just parts of it, sorry? Oh, I just got a message here. The Mars Federation is not, I repeat, not involved in this. It is the factory city Amornia. Yes, that's the north homosphere. Do we have a statement from the Federation yet? No? I see. Yes, the people of Hangar has been adviced to evacuate, and ...” Evelaine turns the radio off. - What a load of crap! - Where are we going? Justin asks. - The moon, I suppose. It's just three ours away. - But the Moon is abandoned! What will we do there? - I don't know! Give me a break, will ya. It's war! And she swollows a bunch of pills again. - You don't want to go back to Earth, do you? she continues. You know they're allies, Earth and Hangar. - Of course I know! That's the very reason I'm out here, to discuss a new trading treaty. - More likely it will be a treaty of war. Justin looks out of the cockpit window. He sees Hangar slowly flowting away, and attack ships fighting agains each other. Yep. So much for my diplomatic mission, he thinks to himself. [Level 2, to the Moon] But the Moon was not abandoned. Yet, the people gathered here was of a rather dubious sort. - Yes, that's what I thought, Evelaine says to that blinking, red button in the cockpit. We need more uranium, if we're ever going to leave this rock. Wait here, ok? I will go and talk with Tokata. - Who is Tokata? - Our best friend!
Boids and asteroids
2012-06-21
Hello! For version 0.0.2 I present some boids and colliding asteroids. For 0.0.3 I'll try to write some lore and story - at least to have some basics of the universe the game is played in.
A new Amiga game
2012-05-10
Hello, and welcome to the webpage of BASIC Shoot-em-up, a new Amiga game developed with BlitzBASIC 2.1 on the real machine. Below is a video on my progress so far.
The main idea is to introduce on Amiga the progress that has been done in physics and AI. The Amiga CPU will of course limit this to some extent, but to a lesser extent than you think.
If you want to contribute with code, graphics, music/sound and ideas, you're very welcome.
Source code to the right. This is version 0.0.1. Version 0.0.2 will include asteroids and flocking behaviour.
;
; New shoot-em-up project
;
; Since 2012-05-30
;
; 0.0.2
;
; observera hur man maste fixa kord nar man blandar sprites
; och shapes i en slice som har storre bitmap an skarmen.
;
; Function prefixes:
; e = enemy
; v = vector
;
WBStartup
;NoCli
LoadPalette 0, "work:palette.iff"
VWait 5
; Game constants
#TIMELINE_LENGTH = 500
#MAX_ASTEROIDS = 10
#MAX_ENEMIES = 15
#TMP_SHAPE = 0
#FOE_SHAPE = 1
#BULLET_SHAPE = 2
#EXPLOSION_SHAPE = 3
#ASTEROID_SHAPE = 4
; Enemy types
#ENEMY_TYPE_BASIC = 0
#ENEMY_TYPE_CHASER = 10
#ENEMY_TYPE_CHASER2 = 20 ; Vector chaser
#ENEMY_TYPE_ROUTER = 30 ; Enemy that follows a path, or route
#ENEMY_TYPE_FLOCKER = 40 ; Enemy with flocking behaviour
; Actions
#ACTION_DISPATCH_ENEMY = 1
#ACTION_DISPATCH_ASTEROID = 2
; Damage types
#DAMAGE_TYPE_LASER
#DAMAGE_TYPE_BULLET
;
; 2D-vector
;
NEWTYPE .vector
x.q
y.q
End NEWTYPE
;
; Some vector functions for vector arithmetic
;
; Add vectors u + v
Statement v_add{*u.vector, *v.vector, *result.vector}
*result\x = *u\x + *v\x
*result\y = *u\y + *v\y
End Statement
; Subtract vectors u - v
Statement v_sub{*u.vector, *v.vector, *result.vector}
*result\x = *u\x - *v\x
*result\y = *u\y - *v\y
End Statement
; Divide vectors, u / v
;Statement v_div{*u.vector, *v.vector, *result.vector}
;End Statement
; Multiply vectors u * v
;Statement v_mul{*u.vector, *v.vector, *result.vector}
;End Statement
;Function.vector v_sub2{*u.vector, *v.vector}
;End Function
; Vector magnitude (returns scalar)
Function v_mag{*v.vector}
mag = Sqr(*v\x * *v\x + *v\y * *v\y)
Function Return mag
End Function
; Multiply vector with scalar
Statement v_scalar_mul{*v.vector, s.q, *result.vector}
*result\x = *v\x * s
*result\y = *v\y * s
End Statement
; Divide vector with scalar
Statement v_scalar_div{*v.vector, s.q, *result.vector}
*result\x = *v\x / s
*result\y = *v\y / s
End Statement
; Normalize vector v
Statement v_norm{*v.vector, *result.vector}
mag = v_mag{*v}
If mag = 0
*result\x = 0, 0
Else
*result\x = *v\x / mag
*result\y = *v\y / mag
EndIf
End Statement
; Dot product (returns scalar)
Function v_dot{*u.vector, *v.vector}
Function Return (*u\x * *v\x + *u\y * *v\y)
End Function
;
; Armor
;
NEWTYPE .armor
shield.q
hull.q
End NEWTYPE
NEWTYPE .damage
shield.q
hull.q
_type.b
radioactivity.q
End NEWTYPE
;
; Players ship
;
NEWTYPE .myship
x.q
y.q
x_speed.q
attackdelay.q
attackdelaylimit.q
acc.q
max_speed.q
y_speed.q
hp.armor
weapon.damage
End NEWTYPE
;
; Players bullets
;
NEWTYPE .bullet
x.q:y:speed
End NEWTYPE
me.myship\x=64,64,3,0,20
me\acc = 1
me\max_speed = 3
me\hp\shield = 100
me\hp\hull = 1000
me\weapon\shield = 1
me\weapon\hull = 3
;
; Path/route for AI to follow
;
;NEWTYPE .route
; points.vector[10] ; Maximum of 10 points
; length.b
;End NEWTYPE
;
; Enemy type
;
NEWTYPE .enemy
_type.b ; byte = +-128
x.q
y.q
x_speed.q
y_speed.q
x_acc.q ; x-acceleration
y_acc.q ; y-acceleration
max_speed.q
hp.armor ; Shield and hull
; Custom memory area, specific for each enemy
extramem_size.q
extramem.l
hit.b ; Has this enemy been hit? Used by blitmode
alive.b ; Is enemy alive? If no, slot free in enemy array
End NEWTYPE
;
; Asteroid type
;
NEWTYPE .asteroid
pos.vector
speed.q
dir.vector ; Direction, normalized vector
alive.b ; True if asteroid is alive. This is needed since we're not using a dim list.
End NEWTYPE
;
; Explosion type
;
NEWTYPE .explosion
x.q
y.q
size.q
End NEWTYPE
;
; Action type
;
; Executed by execute_action{}
; Actions are stored in the level timeline
;
; Action aspects mean different things for different
; action types.
;
; #ACTION_DISPATCH_ENEMY
; aspects[0] = x-coord
; [1] = y-coord
; [2] = x_speed
; [3] = y_speed
; [4] = x_acc
; [5] = y_acc
; [6] = max_speed
; [7] = route length
; [10-30] = points in route
;
NEWTYPE .action
_type.b ; Type of action
value.b ; Value of action, e.g. enemy type
aspects.q[31] ; Actions aspects, e.g. x- and y-coordinates
; Custom memory buffer
extramem_size.q
extramem.l
End NEWTYPE
;
; The level contains of a timeline with actions
;
NEWTYPE .level
*timeline.action[500] ; Actions
length.l ; Timestamp length
End NEWTYPE
;
; Build test level
;
Dim actions.action(100)
ac.action\_type = #ACTION_DISPATCH_ENEMY, #ENEMY_TYPE_BASIC
ac\aspects[0] = 355
ac\aspects[1] = 50
ac\aspects[2] = 2
ac\aspects[3] = 150 ; y_speed used as y offset
ac\aspects[5] = 10 ; y_speed used as amplitude
ac\aspects[7] = 5 ; Shield
ac\aspects[8] = 5 ; hull
ac\extramem_size = 0
ac7.action\_type = #ACTION_DISPATCH_ENEMY, #ENEMY_TYPE_BASIC
ac7\aspects[0] = 355
ac7\aspects[1] = 50
ac7\aspects[2] = 1
ac7\aspects[3] = 75 ; y_speed used as y offset
ac7\aspects[5] = 20 ; y_speed used as amplitude
ac7\extramem_size = 0
ac8.action\_type = #ACTION_DISPATCH_ENEMY, #ENEMY_TYPE_BASIC
ac8\aspects[0] = 355
ac8\aspects[1] = 50
ac8\aspects[2] = 2
ac8\aspects[3] = 0 ; y_speed used as y offset
ac8\aspects[5] = 10 ; y_speed used as amplitude
ac8\extramem_size = 0
ac2.action\_type = #ACTION_DISPATCH_ENEMY, #ENEMY_TYPE_BASIC
ac2\aspects[0] = 355
ac2\aspects[1] = 100
ac2\aspects[2] = 2
ac2\aspects[3] = 50
ac3.action\_type = #ACTION_DISPATCH_ENEMY
ac3\value = #ENEMY_TYPE_CHASER2
ac3\aspects[0] = 355 ; x
ac3\aspects[1] = 50 ; y
ac3\aspects[2] = 0 ; x_speed
ac3\aspects[3] = 0 ; y_speed
ac3\aspects[4] = 0.1
ac3\aspects[5] = 0.1
ac3\aspects[6] = 2
ac3\aspects[7] = 5
ac3\aspects[8] = 7
ac31.action\_type = #ACTION_DISPATCH_ENEMY
ac31\value = #ENEMY_TYPE_CHASER2
ac31\aspects[0] = 355 ; x
ac31\aspects[1] = 125 ; y
ac31\aspects[2] = 0 ; x_speed
ac31\aspects[3] = 0 ; y_speed
ac31\aspects[4] = 0.1
ac31\aspects[5] = 0.1
ac31\aspects[6] = 2
ac31\aspects[7] = 5
ac31\aspects[8] = 7
ac32.action\_type = #ACTION_DISPATCH_ENEMY
ac32\value = #ENEMY_TYPE_CHASER2
ac32\aspects[0] = 355 ; x
ac32\aspects[1] = 200 ; y
ac32\aspects[2] = 0 ; x_speed
ac32\aspects[3] = 0 ; y_speed
ac32\aspects[4] = 0.1
ac32\aspects[5] = 0.1
ac32\aspects[6] = 2
ac32\aspects[7] = 5
ac32\aspects[8] = 7
level1.level\length = 500
; Basics sine
;level1.level\timeline[10] = ac
;level1.level\timeline[11] = ac
;level1.level\timeline[12] = ac
;level1.level\timeline[13] = ac
;level1.level\timeline[14] = ac
;level1.level\timeline[20] = ac7
;level1.level\timeline[21] = ac7
;level1.level\timeline[22] = ac7
;level1.level\timeline[23] = ac7
;level1.level\timeline[24] = ac7
;level1.level\timeline[30] = ac8
;level1.level\timeline[31] = ac8
;level1.level\timeline[32] = ac8
;level1.level\timeline[33] = ac8
;level1.level\timeline[34] = ac8
ac1.action\_type = #ACTION_DISPATCH_ENEMY
ac1\value = #ENEMY_TYPE_CHASER
ac1\aspects[0] = 355 ; x
ac1\aspects[1] = 100 ; y
ac1\aspects[2] = 0 ; x_speed
ac1\aspects[3] = 0 ; y_speed
ac1\aspects[4] = 0.1 ; x_acc
ac1\aspects[5] = 0.1 ; y_acc
ac1\aspects[6] = 2 ; max_speed
ac1\aspects[7] = 5 ; shield
ac1\aspects[8] = 5 ; hull
;level1\timeline[50] = ac1
;level1\timeline[55] = ac1
;level1\timeline[60] = ac1
;level1\timeline[13] = ac1
;level1\timeline[14] = ac1
;level1\timeline[80] = ac3
;level1\timeline[81] = ac31
;level1\timeline[82] = ac32
; Route enemy; also custom memory test
ac4.action\_type = #ACTION_DISPATCH_ENEMY
ac4\value = #ENEMY_TYPE_ROUTER
ac4\aspects[0] = 355 ; x
ac4\aspects[1] = 100 ; y
ac4\aspects[2] = 0 ; x_speed
ac4\aspects[3] = 0 ; y_speed
ac4\aspects[4] = 1 ; x_acc
ac4\aspects[5] = 1 ; y_acc
ac4\aspects[6] = 2 ; max_speed
ac4\aspects[7] = 5 ; shield
ac4\aspects[8] = 7 ; hull
; Route with 2 points = 2 * 2 * 4 = 16 bytes + length + iterator = 24 bytes
; THIS MEMORY MUST BE FREED WHEN THE ENEMY IS DESTROYD!
p.l = AllocMem(40, 1)
Poke.q p + 0, 4 ; Number of points
Poke.q p + 4, 0 ; Iterator for point iteration
Poke.q p + 8, 300 ; x1
Poke.q p + 12, 50 ; y1
Poke.q p + 16, 200 ; x2
Poke.q p + 20, 150 ; y2
Poke.q p + 24, 100 ; x3
Poke.q p + 28, 50 ; y3
Poke.q p + 32, 0 ; x4
Poke.q p + 36, 150 ; y4
ac4\extramem_size = 24
ac4\extramem = p
; Route enemy; also custom memory test
ac5.action\_type = #ACTION_DISPATCH_ENEMY
ac5\value = #ENEMY_TYPE_ROUTER
ac5\aspects[0] = 355 ; x
ac5\aspects[1] = 100 ; y
ac5\aspects[2] = 0 ; x_speed
ac5\aspects[3] = 0 ; y_speed
ac5\aspects[4] = 1 ; x_acc
ac5\aspects[5] = 1 ; y_acc
ac5\aspects[6] = 2 ; max_speed
ac5\aspects[7] = 5 ; shield
ac5\aspects[8] = 7 ; hull
; Route with 2 points = 2 * 2 * 4 = 16 bytes + length + iterator = 24 bytes
; THIS MEMORY MUST BE FREED WHEN THE ENEMY IS DESTROYD!
p.l = AllocMem(40, 1)
Poke.q p + 0, 4 ; Number of points
Poke.q p + 4, 0 ; Iterator for point iteration
Poke.q p + 8, 300 ; x1
Poke.q p + 12, 50 ; y1
Poke.q p + 16, 200 ; x2
Poke.q p + 20, 150 ; y2
Poke.q p + 24, 100 ; x3
Poke.q p + 28, 50 ; y3
Poke.q p + 32, 0 ; x4
Poke.q p + 36, 150 ; y4
ac5\extramem_size = 24
ac5\extramem = p
; Route enemy; also custom memory test
ac6.action\_type = #ACTION_DISPATCH_ENEMY
ac6\value = #ENEMY_TYPE_ROUTER
ac6\aspects[0] = 355 ; x
ac6\aspects[1] = 100 ; y
ac6\aspects[2] = 0 ; x_speed
ac6\aspects[3] = 0 ; y_speed
ac6\aspects[4] = 1.0 ; x_acc
ac6\aspects[5] = 1.0 ; y_acc
ac6\aspects[6] = 2 ; max_speed
ac6\aspects[7] = 5 ; shield
ac6\aspects[8] = 7 ; hull
; Route with 2 points = 2 * 2 * 4 = 16 bytes + length + iterator = 24 bytes
; THIS MEMORY MUST BE FREED WHEN THE ENEMY IS DESTROYD!
p.l = AllocMem(40, 1)
Poke.q p + 0, 4 ; Number of points
Poke.q p + 4, 0 ; Iterator for point iteration
Poke.q p + 8, 300 ; x1
Poke.q p + 12, 50 ; y1
Poke.q p + 16, 200 ; x2
Poke.q p + 20, 150 ; y2
Poke.q p + 24, 100 ; x3
Poke.q p + 28, 50 ; y3
Poke.q p + 32, 0 ; x4
Poke.q p + 36, 150 ; y4
ac6\extramem_size = 24
ac6\extramem = p
;level1\timeline[120] = ac4
;level1\timeline[121] = ac5
;level1\timeline[122] = ac6
; Route enemy; also custom memory test
ac9.action\_type = #ACTION_DISPATCH_ENEMY
ac9\value = #ENEMY_TYPE_ROUTER
ac9\aspects[0] = 355 ; x
ac9\aspects[1] = 100 ; y
ac9\aspects[2] = 0 ; x_speed
ac9\aspects[3] = 0 ; y_speed
ac9\aspects[4] = 0.1 ; x_acc
ac9\aspects[5] = 0.1 ; y_acc
ac9\aspects[6] = 3 ; max_speed
ac9\aspects[7] = 5 ; shield
ac9\aspects[8] = 5 ; hull
; Route with 2 points = 2 * 2 * 4 = 16 bytes + length + iterator = 24 bytes
; THIS MEMORY MUST BE FREED WHEN THE ENEMY IS DESTROYD!
p.l = AllocMem(40, 1)
Poke.q p + 0, 4 ; Number of points
Poke.q p + 4, 0 ; Iterator for point iteration
Poke.q p + 8, 300 ; x1
Poke.q p + 12, 50 ; y1
Poke.q p + 16, 200 ; x2
Poke.q p + 20, 150 ; y2
Poke.q p + 24, 100 ; x3
Poke.q p + 28, 50 ; y3
Poke.q p + 32, 0 ; x4
Poke.q p + 36, 150 ; y4
ac9\extramem_size = 24
ac9\extramem = p
; Route enemy; also custom memory test
ac10.action\_type = #ACTION_DISPATCH_ENEMY
ac10\value = #ENEMY_TYPE_ROUTER
ac10\aspects[0] = 355 ; x
ac10\aspects[1] = 100 ; y
ac10\aspects[2] = 0 ; x_speed
ac10\aspects[3] = 0 ; y_speed
ac10\aspects[4] = 0.1 ; x_acc
ac10\aspects[5] = 0.1 ; y_acc
ac10\aspects[7] = 5 ; shield
ac10\aspects[8] = 5 ; hull
ac10\aspects[6] = 3 ; max_speed
; Route with 2 points = 2 * 2 * 4 = 16 bytes + length + iterator = 24 bytes
; THIS MEMORY MUST BE FREED WHEN THE ENEMY IS DESTROYD!
p.l = AllocMem(40, 1)
Poke.q p + 0, 4 ; Number of points
Poke.q p + 4, 0 ; Iterator for point iteration
Poke.q p + 8, 300 ; x1
Poke.q p + 12, 50 ; y1
Poke.q p + 16, 200 ; x2
Poke.q p + 20, 150 ; y2
Poke.q p + 24, 100 ; x3
Poke.q p + 28, 50 ; y3
Poke.q p + 32, 0 ; x4
Poke.q p + 36, 150 ; y4
ac10\extramem_size = 24
ac10\extramem = p
; Route enemy; also custom memory test
ac11.action\_type = #ACTION_DISPATCH_ENEMY
ac11\value = #ENEMY_TYPE_ROUTER
ac11\aspects[0] = 355 ; x
ac11\aspects[1] = 100 ; y
ac11\aspects[2] = 0 ; x_speed
ac11\aspects[3] = 0 ; y_speed
ac11\aspects[4] = 0.1 ; x_acc
ac11\aspects[5] = 0.1 ; y_acc
ac11\aspects[6] = 3 ; max_speed
ac11\aspects[7] = 5 ; shield
ac11\aspects[8] = 5 ; hull
; Route with 2 points = 2 * 2 * 4 = 16 bytes + length + iterator = 24 bytes
; THIS MEMORY MUST BE FREED WHEN THE ENEMY IS DESTROYD!
p.l = AllocMem(40, 1)
Poke.q p + 0, 4 ; Number of points
Poke.q p + 4, 0 ; Iterator for point iteration
Poke.q p + 8, 300 ; x1
Poke.q p + 12, 50 ; y1
Poke.q p + 16, 200 ; x2
Poke.q p + 20, 150 ; y2
Poke.q p + 24, 100 ; x3
Poke.q p + 28, 50 ; y3
Poke.q p + 32, 0 ; x4
Poke.q p + 36, 150 ; y4
ac11\extramem_size = 24
ac11\extramem = p
; Routers with norm vectors
;level1\timeline[0] = ac9
;level1\timeline[1] = ac10
;level1\timeline[2] = ac11
as01.action\_type = #ACTION_DISPATCH_ASTEROID
level1\timeline[103] = as01
level1\timeline[104] = as01
level1\timeline[105] = as01
level1\timeline[106] = as01
level1\timeline[107] = as01
level1\timeline[113] = as01
level1\timeline[124] = as01
level1\timeline[125] = as01
level1\timeline[126] = as01
level1\timeline[127] = as01
level1\timeline[130] = as01
level1\timeline[134] = as01
level1\timeline[125] = as01
level1\timeline[136] = as01
level1\timeline[137] = as01
level1\timeline[140] = as01
level1\timeline[144] = as01
level1\timeline[145] = as01
level1\timeline[146] = as01
level1\timeline[147] = as01
level1\timeline[150] = as01
level1\timeline[154] = as01
level1\timeline[155] = as01
level1\timeline[156] = as01
level1\timeline[158] = as01
level1\timeline[160] = as01
level1\timeline[164] = as01
level1\timeline[165] = as01
level1\timeline[166] = as01
level1\timeline[168] = as01
; Flocker test
acf.action\_type = #ACTION_DISPATCH_ENEMY
acf\value = #ENEMY_TYPE_FLOCKER
acf\aspects[0] = 355 ; x
acf\aspects[1] = 125 ; y
acf\aspects[2] = 0 ; x_speed
acf\aspects[3] = 0 ; y_speed
acf\aspects[4] = 0.1 ; x acc
acf\aspects[5] = 0.1 ; y acc
acf\aspects[6] = 3 ; max speed
acf\aspects[7] = 5 ; shield
acf\aspects[8] = 7 ; hull
acf2.action\_type = #ACTION_DISPATCH_ENEMY
acf2\value = #ENEMY_TYPE_FLOCKER
acf2\aspects[0] = 355 ; x
acf2\aspects[1] = 25 ; y
acf2\aspects[2] = 0 ; x_speed
acf2\aspects[3] = 0 ; y_speed
acf2\aspects[4] = 0.1 ; x acc
acf2\aspects[5] = 0.1 ; y acc
acf2\aspects[6] = 3 ; max speed, id
acf2\aspects[7] = 5 ; shield
acf2\aspects[8] = 7 ; hull
acf3.action\_type = #ACTION_DISPATCH_ENEMY
acf3\value = #ENEMY_TYPE_FLOCKER
acf3\aspects[0] = 355 ; x
acf3\aspects[1] = 185 ; y
acf3\aspects[2] = 0 ; x_speed
acf3\aspects[3] = 0 ; y_speed
acf3\aspects[4] = 0.1 ; x acc
acf3\aspects[5] = 0.1 ; y acc
acf3\aspects[6] = 3 ; max speed, id
acf3\aspects[7] = 5 ; shield
acf3\aspects[8] = 7 ; hull
acf4.action\_type = #ACTION_DISPATCH_ENEMY
acf4\value = #ENEMY_TYPE_FLOCKER
acf4\aspects[0] = 355 ; x
acf4\aspects[1] = 85 ; y
acf4\aspects[2] = 0 ; x_speed
acf4\aspects[3] = 0 ; y_speed
acf4\aspects[4] = 0.1 ; x acc
acf4\aspects[5] = 0.1 ; y acc
acf4\aspects[6] = 3 ; max speed, id
acf4\aspects[7] = 5 ; shield
acf4\aspects[8] = 7 ; hull
acf5.action\_type = #ACTION_DISPATCH_ENEMY
acf5\value = #ENEMY_TYPE_FLOCKER
acf5\aspects[0] = 355 ; x
acf5\aspects[1] = 95 ; y
acf5\aspects[2] = 0 ; x_speed
acf5\aspects[3] = 0 ; y_speed
acf5\aspects[4] = 0.1 ; x acc
acf5\aspects[5] = 0.1 ; y acc
acf5\aspects[6] = 3 ; max speed, id
acf5\aspects[7] = 5 ; shield
acf5\aspects[8] = 7 ; hull
; Flockers
level1\timeline[1] = acf
level1\timeline[2] = acf2
level1\timeline[3] = acf3
level1\timeline[4] = acf4
level1\timeline[5] = acf5
Dim List bullets.bullet (5)
Dim enemies.enemy(#MAX_ENEMIES)
; Null all enemy slots
For i = 0 To #MAX_ENEMIES
enemies(i)\alive = False
Next
Dim List explosions.explosion(10)
Dim asteroids.asteroid(#MAX_ASTEROIDS)
For i = 0 To #MAX_ASTEROIDS
asteroids(i)\alive = False
Next
;Repeat
; Read x.q, y.q, speed.q
; If AddItem(enemies())
; enemies()\x = x, y, speed, #ENEMY_TYPE_BASIC
; EndIf
;Until x = 20
;asd assd
; Basic enemy data
; x, y and speed
;Data.q 80, 4, 1
;Data.q 100, 4, 1
;Data.q 40, 4, 1
;Data.q 50, 4, 1
;Data.q 60, 4, 1
;Data.q 90, 4, 1
;Data.q 110, 4, 1
;Data.q 120, 4, 1
;Data.q 130, 4, 1
;Data.q 140, 4, 1
;Data.q 150, 4, 1
;Data.q 160, 4, 1
;Data.q 20, 4, 1
;
; Limit x and y to screen
;
Statement e_limit_xy{*e.enemy}
USEPATH *e
If \x < 0 Then \x = 0
If \x > 355 Then \x = 355
If \y < 0 Then \y = 0
If \y > 256 Then \y = 256
End Statement
;
; Updates x and y of basic enemy type
; Sinus movement
;
; y_speed used as offset
; y_acc used as amplitude
;
Statement e_update_basic{*e.enemy}
USEPATH *e
x.q = *e\x
y.q = *e\y
\x = \x + \x_speed
*e\y = ((Sin(x / 8) + 4) * \y_acc) + \y_speed
;Locate 10, 12: Print \y
; Check borders
If \x < 16 Then \x_speed = -(\x_speed)
If \x > 330 + 32 Then \x_speed = -(\x_speed)
End Statement
;
; Update chaser
; Enemy type that chases after player
;
Statement e_update_chaser{*e.enemy, *me.myship}
USEPATH *e
;SHARED me.myship
; Update x_speed
If *me\x > \x-32
\x_speed + \x_acc
EndIf
If *me\x < \x-32
\x_speed - \x_acc
EndIf
; Update y_speed
If *me\y > \y
\y_speed + \y_acc
EndIf
If *me\y < \y
\y_speed - \y_acc
EndIf
\x_speed = QLimit(\x_speed, -\max_speed, \max_speed)
\y_speed = QLimit(\y_speed, -\max_speed, \max_speed)
\x + \x_speed
\y + \y_speed
e_limit_xy{*e}
End Statement
;
; Chaser that uses interception
;
Statement e_update_chaser2{*e.enemy, *player.myship}
USEPATH *e
; Vector positions
Spray.vector\x = *player\x + 32, *player\y
Spredator.vector\x = *e\x, *e\y
; Velocities for pray and predator
Vpray.vector\x = *player\x_speed, *player\y_speed
Vpredator.vector\x = *e\x_speed, *e\y_speed
; Calculate closing velocity
rel_vel.vector\x = *player\x_speed - *e\x_speed
rel_vel\y = *player\y_speed - *e\y_speed
rel_dist.vector\x = 0
v_sub{Spredator, Spray, rel_dist}
; Travel time
t = v_mag{rel_dist} / v_mag{rel_vel}
; Prays velocity Vpray times travel time t
Vpray_t.vector\x = 0
v_scalar_mul{Vpray, t, Vpray_t}
Spray_t.vector\x = 0
v_add{Spray, Vpray_t, Spray_t}
; Relative distance vector
rel_dist2.vector\x = 0
v_sub{Spray_t, Spredator, rel_dist2}
rel_dist2_norm.vector\x = 0
v_norm{rel_dist2, rel_dist2_norm}
max_x = Abs(rel_dist2_norm\x * \max_speed)
max_y = Abs(rel_dist2_norm\y * \max_speed)
; \x_acc = rel_dist2_norm\x
; \y_acc = rel_dist2_norm\y
; \x_acc = QLimit(\x_acc, -\max_speed, \max_speed)
; \y_acc = QLimit(\y_acc, -\max_speed, \max_speed)
;\x_speed + \x_acc
;\y_speed + \y_acc
; Update x_speed
If rel_dist2_norm\x > 0
\x_speed + \x_acc
EndIf
If rel_dist2_norm\x < 0
\x_speed - \x_acc
EndIf
; Update y_speed
If rel_dist2_norm\y > 0
\y_speed + \y_acc
EndIf
If rel_dist2_norm\y < 0
\y_speed - \y_acc
EndIf
;Locate 10, 11: Print \x_speed
\x_speed = QLimit(\x_speed, -max_x, max_x)
\y_speed = QLimit(\y_speed, -max_y, max_y)
\x + \x_speed
\y + \y_speed
e_limit_xy{*e}
;Locate 10, 12: Print " ": Print rel_vel\x
;Locate 10, 13: Print " ": Print rel_vel\y
;Locate 10, 14: Print " ": Print t
End Statement
;
; Update router (enemy that follows path)
;
; Structure of router extra mem (address and content):
; 0 = number of points
; 4 = Iterator
; 8 = x1
; 12 = y1
; 16 = x1
; ...
Statement e_update_router{*e.enemy}
USEPATH *e
SHARED enemies()
; This is an router, so we have to get our buffer and stuff
; First, start with the length of the route
length = Peek.q(\extramem)
; Abort if length = 0
If length = 0
FreeMem \extramem, \extramem_size
;KillItem enemies()
\alive = False
Goto end_route
EndIf
iterator.q = Peek.q(\extramem + 4)
; Abort if this is last iteration
If iterator = length
FreeMem \extramem, \extramem_size
;KillItem enemies()
\alive = False
Goto end_route
EndIf
; Fetch current point
x.q = Peek.q(\extramem + 8 + (iterator * 8))
y.q = Peek.q(\extramem + 12 + (iterator * 8))
If \x < x
\x_speed + \x_acc
EndIf
If \x > x
\x_speed - \x_acc
EndIf
If \y < y
\y_speed + \y_acc
EndIf
If \y > y
\y_speed - \y_acc
EndIf
; Set max_speed to normalized relative distance to point
rel_dist.vector\x = x - \x, y - \y
rel_dist_norm.vector\x = 0
v_norm{rel_dist, rel_dist_norm}
max_x = Abs(rel_dist_norm\x * \max_speed)
max_y = Abs(rel_dist_norm\y * \max_speed)
\x_speed = QLimit(\x_speed, -max_x, max_x)
\y_speed = QLimit(\y_speed, -max_y, max_y)
\x + \x_speed
\y + \y_speed
e_limit_xy{*e}
; Goto next point?
If \x > x - 5 AND \x < x + 5 AND \y > y - 5 AND \y < y + 5
Poke.q \extramem + 4, iterator + 1
Else
EndIf
;Locate 10, 12: Print iterator
end_route:
End Statement
;
; Enemy with flocking behaviour
;
; av_pos = avarage position of all flockers
; av_dir = avarage direction of all flockers (x_speed, y_speed)
;
Statement e_update_flocker{*e.enemy, *av_pos.vector, *av_dir.vector, *player.myship, i}
USEPATH *e
SHARED me, enemies()
; Cohesion
dir.vector\x = \x_speed, \y_speed
rel_dir.vector\x = *av_dir\x - dir\x, *av_dir\y - dir\y
dir_norm.vector\x = 0
v_norm{dir, dir_norm}
rel_dir_norm.vector\x = 0
v_norm{rel_dir, rel_dir_norm}
rel_pos.vector\x = *av_pos\x - *e\x, *av_pos\y - *e\y
rel_pos_norm.vector\x = 0
v_norm{rel_pos, rel_pos_norm}
dot.q = v_dot{rel_pos_norm, dir_norm}
If QAbs(dot) < 1
a.q = ACos(dot) / Pi
; Update x_speed
If rel_pos_norm\x > 0
\x_speed + \x_acc * a * 0.5
EndIf
If rel_pos_norm\x < 0
\x_speed - \x_acc * a * 0.5
EndIf
; Update y_speed
If rel_pos_norm\y > 0
\y_speed + \y_acc * a * 0.5
EndIf
If rel_pos_norm\y < 0
\y_speed - \y_acc * a * 0.5
EndIf
EndIf
; Alignment
dot.q = v_dot{dir_norm, av_dir}
If QAbs(dot) < 1
a.q = ACos(dot) / Pi
; Update x_speed
If rel_dir_norm\x > 0
\x_speed + \x_acc * a * 0.5
EndIf
If rel_dir_norm\x < 0
\x_speed - \x_acc * a * 0.5
EndIf
; Update y_speed
If rel_dir_norm\y > 0
\y_speed + \y_acc * a * 0.5
EndIf
If rel_dir_norm\y < 0
\y_speed - \y_acc * a * 0.5
EndIf
EndIf
; Seperation
For j = 0 To #MAX_ENEMIES ; Check neighbours and keep distance
If enemies(j)\alive AND enemies(j)\_type = #ENEMY_TYPE_FLOCKER AND i <> j
d.vector\x = enemies(j)\x - \x, enemies(j)\y - \y
d_mag = v_mag{d}
d_norm.vector\x = 0
v_norm{d, d_norm}
If d_mag < 30
; Update x_speed
If d\x > 10
\x_speed - \x_acc * 1
EndIf
If d\x < -10
\x_speed + \x_acc * 1
EndIf
; Update y_speed
If d\y > 10
\y_speed - \y_acc * 1
EndIf
If d\y < -10
\y_speed + \y_acc * 1
EndIf
EndIf
If d_mag < 5
; Update x_speed
If d\x > 1
\x_speed - \x_acc * 1
EndIf
If d\x < -1
\x_speed + \x_acc * 1
EndIf
; Update y_speed
If d\y > 1
\y_speed - \y_acc * 1
EndIf
If d\y < -1
\y_speed + \y_acc * 1
EndIf
EndIf
EndIf
Next
; Chase player
; Update x_speed
If *player\x > \x - 32
\x_speed + \x_acc
EndIf
If *player\x < \x - 32
\x_speed - \x_acc
EndIf
; Update y_speed
If *player\y > \y
\y_speed + \y_acc
EndIf
If *player\y < \y
\y_speed - \y_acc
EndIf
;max_x = Abs(dir_norm\x * \max_speed)
;max_y = Abs(dir_norm\y * \max_speed)
\x_speed = QLimit(\x_speed, -\max_speed, \max_speed)
\y_speed = QLimit(\y_speed, -\max_speed, \max_speed)
\x + \x_speed
\y + \y_speed
e_limit_xy{*e}
If \max_speed = 3
;Locate 10, 11: Print dot
;Locate 10, 12: Print rel_pos_norm\x
;Locate 10, 13: Print rel_pos_norm\y
;Locate 10, 14: Print dir_norm\x
;Locate 10, 15: Print dir_norm\y
;Locate 10, 16: Print a
EndIf
End Statement
;
; Update enemy logic
; Dispatch execution depending on enemy type
;
Statement update_enemy_logic{}
SHARED enemies(), bullets(), explosions(), me
; Calculate flocker avarage
flocker_avarage_pos.vector\x = 0, 0
flocker_avarage_dir.vector\x = 0, 0
flocker_n.b = 0
For i.b = 0 To #MAX_ENEMIES
USEPATH enemies(i)
If \alive
If \_type = #ENEMY_TYPE_FLOCKER
flocker_avarage_pos\x + \x
flocker_avarage_pos\y + \y
flocker_avarage_dir\x + \x_speed
flocker_avarage_dir\y + \y_speed
flocker_n + 1
EndIf
EndIf
Next
If flocker_n > 0
flocker_avarage_pos\x / flocker_n
flocker_avarage_pos\y / flocker_n
flocker_avarage_dir\x / flocker_n
flocker_avarage_dir\y / flocker_n
;av_dir_norm.vector\x = 0, 0
;v_norm{flocker_avarage_dir, av_dir_norm}
EndIf
For i.b = 0 To #MAX_ENEMIES
USEPATH enemies(i)
If \alive
;Locate 10, 12: Print "alive" : Print " ": Print i
; Check bullet collition
ResetList bullets()
While NextItem(bullets())
If ShapesHit(#BULLET_SHAPE,bullets()\x,bullets()\y,#FOE_SHAPE,\x,\y)
KillItem bullets()
\hp\shield - me\weapon\shield
\hp\hull - me\weapon\hull
\hit = True
; Enemy dead?
If \hp\shield < 0 OR \hp\hull < 0
; Add explosion
If AddItem(explosions())
x = \x - 4
y = \y - 4
x = QLimit(x, 0, 355)
y = QLimit(y, 0, 235)
explosions()\x = x, y
EndIf
; Check for extramem to free
If \extramem_size > 0
Locate 10, 14: Print \extramem_size
; TODO: Fix this
;FreeMem \extramem, \extramem_size
EndIf
;KillItem enemies()
\alive = False
; Break the loop
Goto _next_enemy
EndIf
EndIf
Wend
; Position update dispatcher
; Select correct update procedure from enemy type
Select \_type
Case #ENEMY_TYPE_BASIC
e_update_basic{enemies(i)}
Case #ENEMY_TYPE_CHASER
e_update_chaser{enemies(i), me}
Case #ENEMY_TYPE_CHASER2
e_update_chaser2{enemies(i), me}
Case #ENEMY_TYPE_ROUTER
e_update_router{enemies(i)}
Case #ENEMY_TYPE_FLOCKER
e_update_flocker{enemies(i), flocker_avarage_pos, flocker_avarage_dir, me, i}
Default
; Unknown enemy type, do nothing
End Select
; The enemy could have been removed from the
; list by the customized code above
_next_enemy:
EndIf
Next
End Statement
;
; Update asteroid logic
; Perform collition between asteroids
;
Statement update_asteroid_logic{}
SHARED asteroids()
i.b = 0
For i = 0 To #MAX_ASTEROIDS
USEPATH asteroids(i)
If \alive = True
; Check for asteroid collision
For j.b = 0 To #MAX_ASTEROIDS
If i <> j ; Don't collide with yourself
If asteroids(j)\alive
If ShapesHit(#ASTEROID_SHAPE, \pos\x, \pos\y, #ASTEROID_SHAPE, asteroids(j)\pos\x, asteroids(j)\pos\y)
; Some math...
; Found this on a university math web page
rel_dist.vector\x = 0
v_sub{asteroids(j)\pos, \pos, rel_dist}
e.vector\x = 0
mag.q = v_mag{rel_dist}
v_scalar_div{rel_dist, mag, e}
u1.vector\x = 0
v_scalar_mul{e, v_dot{\dir, e}, u1}
u2.vector\x = 0
v_scalar_mul{e, v_dot{asteroids(j)\dir, e}, u2}
u1_bis.vector\x = 0
u2_bis.vector\x = 0
v_sub{\dir, u1, u1_bis}
v_sub{asteroids(j)\dir, u2, u2_bis}
v1.vector\x = 0
v2.vector\x = 0
v_add{u2, u1_bis, v1}
v_add{u1, u2_bis, v2}
\dir\x = v1\x
\dir\y = v1\y
asteroids(j)\dir\x = v2\x
asteroids(j)\dir\y = v2\y
; Move on block a little so they don't collide again during next loop iteration
;\pos\x + (\dir\x)
;\pos\y + (\dir\y)
EndIf
EndIf
EndIf
Next
\pos\x + \dir\x
\pos\y + \dir\y
If \pos\x < 0
\alive = False
EndIf
If \pos\y < 0
\alive = False
EndIf
If \pos\y > 256
\alive = False
EndIf
EndIf
Next
End Statement
;
; Runs action @a
; Includes a select type dispatcher for different
; action types
;
Statement execute_action{*a.action}
SHARED enemies(), asteroids()
USEPATH *a
Select \_type
Case #ACTION_DISPATCH_ENEMY
For i.b = 0 To #MAX_ENEMIES
If enemies(i)\alive = False
enemies(i)\_type = \value
enemies(i)\x = \aspects[0]
enemies(i)\y = \aspects[1]
enemies(i)\x_speed = \aspects[2]
enemies(i)\y_speed = \aspects[3]
enemies(i)\x_acc = \aspects[4]
enemies(i)\y_acc = \aspects[5]
enemies(i)\max_speed = \aspects[6]
enemies(i)\hp\shield = \aspects[7]
enemies(i)\hp\hull = \aspects[8]
enemies(i)\alive = True
If \extramem_size > 0
enemies(i)\extramem_size = \extramem_size
enemies(i)\extramem = \extramem
EndIf
Goto _end_add_loop
EndIf
Next
_end_add_loop:
Case #ACTION_DISPATCH_ASTEROID
For i = 0 To #MAX_ASTEROIDS
USEPATH asteroids(i)
If \alive = False
; \alive = True
\pos\x = 350, Rnd(200)
\speed = Rnd(1) + 3
\dir\x = (Rnd - 1) - 1, Rnd(2) - 1
For j.b = 0 To #MAX_ASTEROIDS
If asteroids(j)\alive
If ShapesHit(#ASTEROID_SHAPE, asteroids(j)\pos\x, asteroids(j)\pos\y, #ASTEROID_SHAPE, \pos\x, \pos\y)
\alive = False
Goto end_execute_action
EndIf
EndIf
Next
; No collision detected, spawn asteroid
\alive = True
Goto end_execute_action
EndIf
Next
Default
; Unknown action type, do nothing (or better still, signal an error - how?)
End Select
end_execute_action:
End Statement
;
; Assumes BLITZ-mode is on, and other game init like graphics
; is loaded
;
Statement run_level{*l.level}
SHARED bullets(), enemies(), explosions(), asteroids(), me
; display setup
;BitMap 0,320+64,256,5
;BitMap 1,320+64,256,5
;Slice 0,44,320,256,$fff9A,5,8,32,320+64,320+64 ; borde vara $fff8A och inte 9A. varfor funkar det?
; Some colors
;ShowPalette 0
;Mouse On
db=1
;Queue 0,20
;Queue 1,20
BLITZ
Repeat
; Flip buffer and empty shape queue
VWait
Show db,32,32
db=1-db
UnQueue db
Use BitMap db
; Tick gametime
; 50 each second
gametime_sub + 1
If gametime_sub > 15
Locate 10, 10:Print gametime
If gametime < *l\length
execute_action{*l\timeline[gametime]}
EndIf
gametime + 1
gametime_sub = 0
EndIf
; ME
USEPATH me
If \attackdelay>0 Then \attackdelay-1 ; minska raknaren for tommy-gun...
Select Joyx(1)
Case 1
\x_speed + \acc
Case -1
\x_speed - \acc
Default
\x_speed = 0
End Select
Select Joyy(1)
Case 1
\y_speed + \acc
Case -1
\y_speed - \acc
Default
\y_speed = 0
End Select
\y_speed = QLimit(\y_speed, -\max_speed, \max_speed)
\x_speed = QLimit(\x_speed, -\max_speed, \max_speed)
\acc = 1
;Locate 10, 14: Print \max_speed
\x + \x_speed
\y + \y_speed
\x=QLimit(\x,0,320-16-\x_speed)
\y=QLimit(\y,32,256-8-\y_speed)
ShowSprite 0,\x,\y - 32,0
; BULLETS
If Joyb(1)=0 Then \attackdelay=0
If Joyb(1)=1 AND \attackdelay=0 ; nytt skott om knapptryckning och nollad attackdelay
If AddItem(bullets())
bullets()\x=\x+32+8,\y,4 ; obs: usepath fortfarande me
\attackdelay=\attackdelaylimit
EndIf
EndIf
ResetList bullets()
While NextItem(bullets()) ; remove bullets at side of screen
bullets()\x+bullets()\speed
QBlit db,#BULLET_SHAPE,bullets()\x,bullets()\y
If bullets()\x>350 Then KillItem bullets()
Wend
;
; Update asteroid logic
;
update_asteroid_logic{}
;
; Update enemy logic
;
update_enemy_logic{}
; Asteroid blitting
For i = 0 To #MAX_ASTEROIDS
If asteroids(i)\alive
QBlit db, #ASTEROID_SHAPE, asteroids(i)\pos\x, asteroids(i)\pos\y
EndIf
Next
;
; Loop through enemies, perform blitting
;
For i = 0 To #MAX_ENEMIES
; Blit shape
USEPATH enemies(i)
If \alive
; Locate 10, 13: Print "blit": Print \y
If \hit = False
QBlit db, #FOE_SHAPE, \x, \y
EndIf
If \hit = True
QBlitMode SolidMode
QBlit db, #FOE_SHAPE, \x, \y
QBlitMode CookieMode
\hit = False
EndIf
EndIf
Next
; Render explosions
ResetList explosions()
While NextItem(explosions())
USEPATH explosions()
QBlit db, #EXPLOSION_SHAPE, \x, \y
KillItem explosions()
Wend
Until RawStatus($45)
End Statement
;
; Setup data
;
BLITZ
;
; Setup palette
;
;
; Setup shapes
;
; Space ship
BitMap 0,16,16,4
Boxf 0,0,16,8,1
GetaShape 0,0,0,16,8
GetaSprite 0,0
; mus test sprite
BitMap 0,32,32,5
Cls
Boxf 0,0,8,8,2
GetaShape 0,0,0,8,8
;GetaSprite 2,0
; fiende-shape
Boxf 0,0,8,8,7
GetaShape #FOE_SHAPE,0,0,8,8
PalRGB 0, 2, 10, 10, 10
;bullet shape
Cls
Boxf 0,0,4,2,8
GetaShape #BULLET_SHAPE,0,0,4,2
; Explosion shape
Cls
Circlef 8, 8, 7, 5
GetaShape #EXPLOSION_SHAPE, 0, 0, 16, 16
; Asteroid shape
Cls
Circlef 16,16,16,10
GetaShape #ASTEROID_SHAPE, 0, 0, 32, 32
; display setup
BitMap 0,320+64,256+64,5
BitMap 1,320+64,256+64,5
Slice 0,44,320,222,$fff9A,5,8,32,320+64,320+64 ; borde vara $fff8A och inte 9A. varfor funkar det?
; Some colors
ShowPalette 0
PalRGB 0, 2, 10, 10, 10
Mouse On
db=1
Queue 0,40
Queue 1,40
; foe test
x=320
y=64
speed=-2
; stjarnor...?
For n=0 To 128
Plot Rnd(300),Rnd(300),Rnd(31)
Next
BitMapOutput 0
Locate 0, 0
; MAIN LOOP
gametime.l = 0
gametime_sub.w = 0
run_level{level1}
VWait 5
End