1
+ love .filesystem .load (" defs.lua" )()
2
+ function newCell (x , y )
3
+ cell = {}
4
+ cell .nucleus = {x = x ,y = y ,vx = 0 ,vy = 0 ,ax = 0 ,ay = 0 }
5
+
6
+ cell .membrane = {}
7
+ for i = 1 , 23 , 2 do
8
+ cell .membrane [i ] = x + 50 * math.cos (math.rad (15 * i ))
9
+ cell .membrane [i + 1 ] = y + 50 * math.sin (math.rad (15 * i ))
10
+ end
11
+ cell .mbsize = table .getn (cell .membrane )
12
+
13
+ cell .springs = {}
14
+ for i = 1 ,13 do cell .springs [i ] = fNucSpringLength (cell ) end
15
+ cell .membraneVel = { }
16
+ for i = 1 ,cell .mbsize do
17
+ cell .membraneVel [i ] = 0 -- set initial velocities to zero
18
+ end
19
+ -- membraneVel[1] = 0.5
20
+ -- membraneVel[2] = 0.5
21
+ cell .membraneAcc = { }
22
+ for i = 1 ,cell .mbsize do
23
+ cell .membraneAcc [i ] = 0 -- set initial acceleration to zero
24
+ end
25
+ cell .genes = {}
26
+ cell .genes .growtime = 2 -- 1 --time to grow a new node, in seconds
27
+ cell .genes .splitnodes = 18 -- 18 --# membrane nodes to divide at
28
+ cell .genes .speed = 15 -- 15 --movement speed
29
+ cell .genes .attackdist = 100 -- how close it has to be to player to attack
30
+ cell .genes .bombgrav = 0 -- how attracted (+) or repelled (-) it is by bombs
31
+ cell .genes .attackstyle = " bump" -- either "bump" or "engulf"
32
+ cell .genes .acidity = 0 -- how much player is damaged when inside cell
33
+ cell .genes .damagestyle = " shrink" -- either "shrink" or "split"
34
+
35
+ cell .gtimer = 0 -- in seconds
36
+ cell .dir = math.random ()* 2 * math.pi -- movement direction
37
+
38
+ return cell
39
+ end
40
+
41
+ function mutate (c )
42
+ local g = c .genes
43
+ -- g.growtime = g.growtime + (math.random()-0.5)*0.01
44
+ if 1 == math.random (10 ) then g .growtime = g .growtime + math.random (3 ) - 2 end
45
+ if (g .growtime < 0.1 ) then g .growtime = 0.1 end
46
+ if 1 == math.random (6 ) then g .splitnodes = g .splitnodes + 2 * (math.random (3 ) - 2 ) end
47
+ if (g .splitnodes < 12 ) then g .splitnodes = 12 end
48
+ -- g.speed = g.speed + (math.random()-0.5)
49
+ -- g.attackdist = g.attackdist + (math.random()-0.5)*10
50
+ -- c.genes.bombgrav = 0
51
+ -- c.genes.attackstyle = "bump"
52
+ -- g.acidity = g.acidity + math.random(-5,5)
53
+ -- c.genes.damagestyle = "shrink"
54
+ end
55
+
56
+ function updateCell (_n ,dt )
57
+ local c = cells [_n ]
58
+ -- print(_n,c)
59
+ -- print(c.membrane[1],c.membrane[2],c.membrane[mbsize-1],c.membrane[mbsize])
60
+ -- print(c.nucleus.x,c.nucleus.y,c.nucleus.vx,c.nucleus.vy,c.nucleus.ax,c.nucleus.ay)
61
+ c .gtimer = c .gtimer + dt
62
+ if c .gtimer >= c .genes .growtime then -- grow
63
+ c .gtimer = c .gtimer - c .genes .growtime -- reset timer
64
+ -- insert new node into random part of membrane:
65
+ local idx = math.random (c .mbsize - 1 )
66
+ local idx2 = idx - 2
67
+ if idx2 < 1 then idx2 = c .mbsize - 1 end
68
+ local nx = (c .membrane [idx2 ]+ c .membrane [idx ])/ 2 -- average of surrounding node x coords
69
+ local ny = (c .membrane [idx2 + 1 ]+ c .membrane [idx + 1 ])/ 2 -- " " y coords
70
+ table.insert (c .membrane ,idx ,nx )
71
+ table.insert (c .membrane ,idx + 1 ,ny )
72
+ local nvx = (c .membraneVel [idx2 ]+ c .membraneVel [idx ])/ 2 -- average of surrounding node vx coords
73
+ local nvy = (c .membraneVel [idx2 + 1 ]+ c .membraneVel [idx + 1 ])/ 2 -- " " vy coords
74
+ table.insert (c .membraneVel ,idx ,nvx )
75
+ table.insert (c .membraneVel ,idx + 1 ,nvy )
76
+ local nodax = (c .membraneAcc [idx2 ]+ c .membraneAcc [idx ])/ 2 -- average of surrounding node ax coords
77
+ local noday = (c .membraneAcc [idx2 + 1 ]+ c .membraneAcc [idx + 1 ])/ 2 -- " " ay coords
78
+ table.insert (c .membraneAcc ,idx ,nodax )
79
+ table.insert (c .membraneAcc ,idx + 1 ,noday )
80
+ -- add spring to nucleus:
81
+ table.insert (c .springs ,(idx + 1 )/ 2 ,fNucSpringLength (c ))
82
+ c .mbsize = table .getn (c .membrane )
83
+ end
84
+
85
+ -- BEGIN MITOSIS:
86
+ if c .mbsize / 2 >= c .genes .splitnodes then -- mitosis!
87
+ --[[ io.write("c: {")
88
+ for itr = 1,c.mbsize do io.write(c.membrane[itr]," ") end
89
+ io.write("}\n")]]
90
+ local split = (c .mbsize / 2 )+ 1 -- divide in two; currently just midpoint; should do random split maybe?
91
+ print (" split:" ,split )
92
+ local oldc = c
93
+ io.write (" oldc: {" )
94
+ for itr = 1 ,oldc .mbsize do io.write (oldc .membrane [itr ]," " ) end
95
+ io.write (" }\n " )
96
+ -- print(oldc)
97
+ c = {}
98
+ c .membrane = {}
99
+ -- print(c,oldc)
100
+ c .membraneVel = {}
101
+ c .membraneAcc = {}
102
+ c .springs = {}
103
+ c .nucleus = {}
104
+ local newcell = {}
105
+ newcell .membrane = {}
106
+ newcell .membraneVel = {}
107
+ newcell .membraneAcc = {}
108
+ newcell .springs = {}
109
+ newcell .nucleus = {}
110
+ for i = 1 ,split - 1 do
111
+ -- print(oldc.membrane[i])
112
+ c .membrane [i ] = oldc .membrane [i ]
113
+ -- print(c.membrane[i])
114
+ c .membraneVel [i ] = oldc .membraneVel [i ]
115
+ c .membraneAcc [i ] = oldc .membraneAcc [i ]
116
+ --[[ if (math.floor(i/2) ~= i/2) then
117
+ c.springs[(i+1)/2] = oldc.springs[(i+1)/2]
118
+ end]]
119
+ end
120
+ for i = split ,oldc .mbsize do
121
+ newcell .membrane [i + 1 - split ] = oldc .membrane [i ]
122
+ -- print(newcell.membrane[i+1-split])
123
+ newcell .membraneVel [i + 1 - split ] = oldc .membraneVel [i ]
124
+ newcell .membraneAcc [i + 1 - split ] = oldc .membraneAcc [i ]
125
+ --[[ if (math.floor(i/2) ~= i/2) then
126
+ newcell.springs[(i+2-split)/2] = oldc.springs[(i+1)/2]
127
+ end]]
128
+ --[[ table.insert(newcell.membrane,i,table.remove(c.membrane,i))
129
+ table.insert(newcell.membraneVel,i,table.remove(c.membraneVel,i))
130
+ table.insert(newcell.membraneAcc,i,table.remove(c.membraneAcc,i))
131
+ if (math.floor(i/2) ~= i/2) then --since springs array has half as many elements
132
+ table.insert(newcell.springs,(i+1)/2,table.remove(c.springs,(i+1)/2))
133
+ end]]
134
+ end
135
+ -- add extra node
136
+ c .membrane [split ] = oldc .nucleus .x
137
+ c .membrane [split + 1 ] = oldc .nucleus .y
138
+ c .membraneVel [split ] = oldc .nucleus .vx
139
+ c .membraneVel [split + 1 ] = oldc .nucleus .vy
140
+ c .membraneAcc [split ] = oldc .nucleus .ax
141
+ c .membraneAcc [split + 1 ] = oldc .nucleus .ay
142
+
143
+ --[[ newcell.membrane[oldc.mbsize+1-split] = oldc.nucleus.x
144
+ newcell.membrane[oldc.mbsize+1-split+1] = oldc.nucleus.y
145
+ newcell.membraneVel[(oldc.mbsize+1)-split] = oldc.nucleus.vx
146
+ newcell.membraneVel[(oldc.mbsize+2)-split] = oldc.nucleus.vy
147
+ newcell.membraneAcc[(oldc.mbsize+1)-split] = oldc.nucleus.ax
148
+ newcell.membraneAcc[(oldc.mbsize+2)-split] = oldc.nucleus.ay]]
149
+
150
+ c .mbsize = table .getn (c .membrane )
151
+ for itr = 1 ,c .mbsize / 2 do c .springs [itr ] = fNucSpringLength (c ) end
152
+ io.write (" c: {" )
153
+ for itr = 1 ,c .mbsize do io.write (c .membrane [itr ]," " ) end
154
+ io.write (" }\n " )
155
+ newcell .mbsize = table .getn (newcell .membrane )
156
+
157
+ newcell .membrane [newcell .mbsize + 1 ] = oldc .nucleus .x
158
+ newcell .membrane [newcell .mbsize + 2 ] = oldc .nucleus .y
159
+ newcell .membraneVel [newcell .mbsize + 1 ] = oldc .nucleus .vx
160
+ newcell .membraneVel [newcell .mbsize + 2 ] = oldc .nucleus .vy
161
+ newcell .membraneAcc [newcell .mbsize + 1 ] = oldc .nucleus .ax
162
+ newcell .membraneAcc [newcell .mbsize + 2 ] = oldc .nucleus .ay
163
+
164
+ newcell .mbsize = table .getn (newcell .membrane )
165
+ for itr = 1 ,newcell .mbsize / 2 do newcell .springs [itr ] = fNucSpringLength (newcell ) end
166
+ print (" n mbsz" ,newcell .mbsize )
167
+ io.write (" newcell: {" )
168
+ for itr = 1 ,newcell .mbsize do io.write (newcell .membrane [itr ]," " ) end
169
+ io.write (" }\n " )
170
+ local cnx = 0
171
+ local cny = 0
172
+ local j = 1
173
+ while j < c .mbsize do
174
+ cnx = cnx + c .membrane [j ]
175
+ cny = cny + c .membrane [j + 1 ]
176
+ j = j + 2
177
+ end
178
+ --[[ newcell.nucleus.x = cnx/(c.mbsize/2)
179
+ newcell.nucleus.y = cny/(c.mbsize/2)]]
180
+ c .nucleus .x = cnx / (c .mbsize / 2 )
181
+ c .nucleus .y = cny / (c .mbsize / 2 )
182
+ c .nucleus .vx = oldc .nucleus .vx -- / 2
183
+ c .nucleus .vy = oldc .nucleus .vy -- / 2
184
+ c .nucleus .ax = oldc .nucleus .ax -- 0
185
+ c .nucleus .ay = oldc .nucleus .ay -- 0
186
+ local nnx = 0
187
+ local nny = 0
188
+ local j = 1
189
+ while j < newcell .mbsize do
190
+ nnx = nnx + newcell .membrane [j ]
191
+ nny = nny + newcell .membrane [j + 1 ]
192
+ j = j + 2
193
+ end
194
+ --[[ c.nucleus.x = nnx/(newcell.mbsize/2)
195
+ c.nucleus.y = nny/(newcell.mbsize/2)]]
196
+ newcell .nucleus .x = nnx / (newcell .mbsize / 2 )
197
+ newcell .nucleus .y = nny / (newcell .mbsize / 2 )
198
+ newcell .nucleus .vx = oldc .nucleus .vx -- / 2
199
+ newcell .nucleus .vy = oldc .nucleus .vy -- / 2
200
+ newcell .nucleus .ax = oldc .nucleus .ax -- 0
201
+ newcell .nucleus .ay = oldc .nucleus .ay -- 0
202
+
203
+ -- TODO: mutations
204
+ c .genes = {}
205
+ c .gtimer = 0
206
+ newcell .genes = {}
207
+ newcell .gtimer = 0
208
+
209
+ for k ,v in pairs (oldc .genes ) do
210
+ c .genes [k ] = v
211
+ newcell .genes [k ] = v
212
+ end
213
+
214
+ mutate (c )
215
+ mutate (newcell )
216
+ -- c.genes.acidity = c.genes.acidity + math.random(-5,5)
217
+ -- newcell.genes.acidity = newcell.genes.acidity + math.random(-5,5)
218
+
219
+ c .dir = math.random ()* 2 * math.pi
220
+ newcell .dir = math.random ()* 2 * math.pi
221
+
222
+ cells [_n ] = c
223
+ --[[ nCells = nCells + 1
224
+ cells[nCells] = newcell]]
225
+ table.insert (cells ,newcell )
226
+ end
227
+ -- END MITOSIS.
228
+
229
+ -- c.nucleus acc
230
+ local nax = 0
231
+ local nay = 0
232
+
233
+ -- acceleration for constant speed
234
+ local acc = mediumDamping * c .genes .speed
235
+
236
+ if distance (c .nucleus .x ,c .nucleus .y ,player .x ,player .y ) <= c .genes .attackdist then
237
+ c .dir = math.atan2 (player .y - c .nucleus .y ,player .x - c .nucleus .x )
238
+ end
239
+ local dir = c .dir
240
+ --[[ if love.keyboard.isDown("right","f","left","s","down","d","up","e") then
241
+ acc = mediumDamping * c.genes.speed
242
+ if love.keyboard.isDown("right","f") then dir = 0 end
243
+ if love.keyboard.isDown("left","s") then dir = math.pi end
244
+ if love.keyboard.isDown("down","d") then dir = 0.5*math.pi end
245
+ if love.keyboard.isDown("up","e") then dir = 1.5*math.pi end
246
+ end]]
247
+ nax = nax + acc * math.cos (dir )
248
+ nay = nay + acc * math.sin (dir )
249
+
250
+ local avgx = 0
251
+ local avgy = 0
252
+ -- Verlet integration
253
+ local i = 1
254
+ while i < c .mbsize do
255
+ local continue = false
256
+ local newax = 0
257
+ local neway = 0
258
+ avgx = avgx + c .membrane [i ]
259
+ avgy = avgy + c .membrane [i + 1 ]
260
+ -- check for COLLISIONS:
261
+ -- with player:
262
+ if distance (c .membrane [i ],c .membrane [i + 1 ],player .x ,player .y ) < plen + 2 then
263
+ hitPlayer (1 )
264
+ table.insert (debugPts ,{x = c .membrane [i ], y = c .membrane [i + 1 ], r = 2 })
265
+ table.insert (debugPts ,{x = c .nucleus .x , y = c .nucleus .y , r = 4 })
266
+ end
267
+ -- with bullets:
268
+ local j = 1
269
+ while j <= table .getn (bullets ) do
270
+ if distance (c .membrane [i ],c .membrane [i + 1 ],bullets [j ].x ,bullets [j ].y ) < 4 then
271
+
272
+ table.insert (debugPts ,{x = c .membrane [i ], y = c .membrane [i + 1 ], r = 2 })
273
+ table.insert (debugPts ,{x = c .nucleus .x , y = c .nucleus .y , r = 4 })
274
+
275
+ table.remove (bullets ,j )
276
+ table.remove (c .membrane ,i )
277
+ table.remove (c .membrane ,i )
278
+ table.remove (c .membraneVel ,i )
279
+ table.remove (c .membraneVel ,i )
280
+ table.remove (c .membraneAcc ,i )
281
+ table.remove (c .membraneAcc ,i )
282
+ table.remove (c .springs ,(i + 1 )/ 2 )
283
+ c .mbsize = table .getn (c .membrane )
284
+ continue = true
285
+ break
286
+ else
287
+ j = j + 1
288
+ end
289
+ end
290
+ if not continue then
291
+ -- update position:
292
+ -- print(i,c.mbsize)
293
+ c .membrane [i ] = c .membrane [i ] + c .membraneVel [i ]* dt + 0.5 * c .membraneAcc [i ]* dt * dt -- update x
294
+ c .membrane [i + 1 ] = c .membrane [i + 1 ] + c .membraneVel [i + 1 ]* dt + 0.5 * c .membraneAcc [i + 1 ]* dt * dt -- update y
295
+ -- boundaries:
296
+ if c .membrane [i ] < xmin then c .membrane [i ] = xmin + 2 ; c .dir = c .dir + math.pi
297
+ elseif c .membrane [i ] > xmax then c .membrane [i ] = xmax - 2 ; c .dir = c .dir + math.pi end
298
+ if c .membrane [i + 1 ] < ymin then c .membrane [i + 1 ] = ymin + 2 ; c .dir = c .dir + math.pi
299
+ elseif c .membrane [i + 1 ] > ymax then c .membrane [i + 1 ] = ymax - 2 ; c .dir = c .dir + math.pi end
300
+ -- calculating acceleration:
301
+
302
+ local accn = acc + 80 * (math.random () - 0.5 )
303
+ newax = newax + accn * math.cos (dir )
304
+ neway = neway + accn * math.sin (dir )
305
+
306
+ -- print(c.springs[(i+1)/2])
307
+
308
+ -- Hooke's law for spring connecting c.membrane point to c.nucleus:
309
+ local distance = math.sqrt ((c .membrane [i ]- c .nucleus .x )^ 2 + (c .membrane [i + 1 ]- c .nucleus .y )^ 2 )
310
+ local force = - kNucSpring * (distance - c .springs [(i + 1 )/ 2 ]) -- -kNucSpring*(distance-nucSpringLength)
311
+ -- if 1 == i or 13 == i then force = -kNucSpring*(distance-nucSpringLength*1.75) end
312
+ local theta = math.atan2 (c .membrane [i + 1 ]- c .nucleus .y ,c .membrane [i ]- c .nucleus .x )
313
+ newax = newax + (force / distance )* math.cos (theta )
314
+ neway = neway + (force / distance )* math.sin (theta )
315
+ nax = nax - --[[ 0.5*]] (force / distance )* math.cos (theta )
316
+ nay = nay - --[[ 0.5*]] (force / distance )* math.sin (theta )
317
+
318
+ -- Hooke's law for springs connecting to adjacent points:
319
+ -- preceding:
320
+ local otherx = 0
321
+ local othery = 0
322
+ -- print(i,c.mbsize)
323
+ if 1 == i then
324
+ otherx = c .membrane [c .mbsize - 1 ]
325
+ othery = c .membrane [c .mbsize ]
326
+ else
327
+ otherx = c .membrane [i - 2 ]
328
+ othery = c .membrane [i - 1 ]
329
+ end
330
+ distance = math.sqrt ((c .membrane [i ]- otherx )^ 2 + (c .membrane [i + 1 ]- othery )^ 2 )
331
+ force = - kMemSpring * (distance - memSpringLength )
332
+ theta = math.atan2 (c .membrane [i + 1 ]- othery ,c .membrane [i ]- otherx )
333
+ newax = newax + (force / distance )* math.cos (theta )
334
+ neway = neway + (force / distance )* math.sin (theta )
335
+
336
+ -- succeeding:
337
+ if c .mbsize - 1 == i then
338
+ otherx = c .membrane [1 ]
339
+ othery = c .membrane [2 ]
340
+ else
341
+ otherx = c .membrane [i + 2 ]
342
+ othery = c .membrane [i + 3 ]
343
+ end
344
+ distance = math.sqrt ((c .membrane [i ]- otherx )^ 2 + (c .membrane [i + 1 ]- othery )^ 2 )
345
+ force = - kMemSpring * (distance - memSpringLength )
346
+ theta = math.atan2 (c .membrane [i + 1 ]- othery ,c .membrane [i ]- otherx )
347
+ newax = newax + (force / distance )* math.cos (theta )
348
+ neway = neway + (force / distance )* math.sin (theta )
349
+
350
+ -- damping:
351
+ newax = newax - mediumDamping * c .membraneVel [i ]
352
+ neway = neway - mediumDamping * c .membraneVel [i + 1 ]
353
+
354
+ -- update velocity:
355
+ c .membraneVel [i ] = c .membraneVel [i ] + (c .membraneAcc [i ]+ newax )* dt / 2 -- update vx
356
+ c .membraneVel [i + 1 ] = c .membraneVel [i + 1 ] + (c .membraneAcc [i + 1 ]+ neway )* dt / 2 -- update vy
357
+ c .membraneAcc [i ] = newax
358
+ c .membraneAcc [i + 1 ] = neway
359
+ i = i + 2
360
+ end -- if not continue; hackish workaround since Lua apparently doesn't have continue
361
+ end
362
+
363
+ avgx = avgx / table .getn (c .membrane )
364
+ avgy = avgy / table .getn (c .membrane )
365
+
366
+ --[[ if distance(avgx,avgy,c.nucleus.x,c.nucleus.y) > nucSpringLength then
367
+ c.nucleus.x = avgx
368
+ c.nucleus.y = avgy
369
+ end]]
370
+
371
+ -- c.nucleus motion: (verlet)
372
+ c .nucleus .x = c .nucleus .x + c .nucleus .vx * dt + 0.5 * c .nucleus .ax * dt * dt
373
+ c .nucleus .y = c .nucleus .y + c .nucleus .vy * dt + 0.5 * c .nucleus .ay * dt * dt
374
+
375
+ -- damping:
376
+ c .nucleus .ax = c .nucleus .ax - mediumDamping * c .nucleus .vx
377
+ c .nucleus .ay = c .nucleus .ay - mediumDamping * c .nucleus .vy
378
+
379
+ c .nucleus .vx = c .nucleus .vx + (c .nucleus .ax + nax )* dt / 2
380
+ c .nucleus .vy = c .nucleus .vy + (c .nucleus .ay + nay )* dt / 2
381
+ c .nucleus .ax = nax
382
+ c .nucleus .ay = nay
383
+ end
0 commit comments