DEADSOFTWARE

Small refactoring of physics module
[cavecraft.git] / src / Mobs.pas
1 unit Mobs;
3 interface
5 const
6 (* Типы мобов *)
7 none = 0;
8 zomby = 1;
10 procedure Create(typ, x, y : integer);
11 function FindAndHit(value, x, y, w, h, addvx, addvy : integer) : integer;
13 (* Части главного цикла *)
14 procedure Update;
15 procedure UpdatePhy;
16 procedure Draw(camx, camy : integer);
18 (* Управление сохранениями *)
19 procedure SaveData;
20 procedure LoadData;
21 procedure ResetData;
23 (* Управление текстурами *)
24 procedure LoadTextures(path : string);
25 procedure FreeTextures;
27 implementation
29 uses phy, player, canvas, jsr75i, func, drop, items;
31 const
32 lastType = 1;
33 lastMob = 31;
34 lastZombyFrame = 11;
36 var
37 mtype, mx, my, mvx, mvy, mpos, mhp, ma, mb, mc : array [0..lastMob] of integer;
38 mjump : array [0..lastMob] of boolean;
39 tw, th, thp, tjump : array [none..lastType] of integer;
41 zombyBody : array [0..1] of image;
42 zombyLegs : array [0..1, 0..2] of image;
43 zombyAnim : array [0..lastZombyFrame] of integer;
45 procedure UseObject(i : integer);
46 begin
47 Phy.SetObject(mx[i], my[i], tw[mtype[i]], th[mtype[i]], mvx[i], mvy[i], mjump[i]);
48 end;
50 procedure UpdateObject(i : integer);
51 begin
52 mx[i] := Phy.GetX;
53 my[i] := Phy.GetY;
54 mvx[i] := Phy.GetVX;
55 mvy[i] := Phy.GetVY;
56 mjump[i] := Phy.GetJump;
57 end;
59 procedure InitTab(typ, w, h, hp, jump : integer);
60 begin
61 assert((typ >= 0) and (typ <= lastType));
62 tw[typ] := w;
63 th[typ] := h;
64 thp[typ] := hp;
65 tjump[typ] := jump;
66 end;
68 function GetW(i : integer) : integer;
69 begin
70 result := tw[mtype[i]];
71 end;
73 function GetH(i : integer) : integer;
74 begin
75 result := th[mtype[i]];
76 end;
78 function GetMaxHp(i : integer) : integer;
79 begin
80 result := thp[mtype[i]];
81 end;
83 function GetJumpHeight(i : integer) : integer;
84 begin
85 result := tjump[mtype[i]];
86 end;
88 function IsSolidStep(i, vec : integer) : boolean;
89 var
90 x, y : integer;
91 begin
92 if vec < 0 then x := (mx[i] - 8) / 16;
93 else if vec > 0 then x := (mx[i] + GetW(i) + 4) / 16;
94 else x := (mx[i] + GetW(i) / 2) / 16;
95 y := (my[i] + GetH(i) - 8) / 16;
96 result := Phy.IsSolid(x, y);
97 end;
99 procedure Swim(i, vec : integer);
100 begin
101 if Phy.AreaWithBlock(50, mx[i], my[i], GetW(i), GetH(i) / 2) or
102 Phy.AreaWithBlock(51, mx[i], my[i], GetW(i), GetH(i) / 2)
103 then begin
104 if (vec <> 0) and IsSolidStep(i, vec) then mvy[i] := -10 else mvy[i] := -2;
105 end;
106 end;
108 procedure Step(i, vec : integer);
109 begin
110 Swim(i, vec);
111 if vec < 0 then
112 begin
113 mvx[i] := mvx[i] + vec;
114 mpos[i] := 0;
115 end
116 else if vec > 0 then
117 begin
118 mvx[i] := mvx[i] + vec;
119 mpos[i] := 1;
120 end;
121 end;
123 procedure Jump(i, vec : integer);
124 begin
125 UseObject(i);
126 Phy.Jump(GetJumpHeight(i));
127 UpdateObject(i);
128 end;
130 function CollisionWithPlayer(i : integer) : boolean;
131 begin
132 result := Phy.IntersectRects(
133 mx[i], my[i], GetW(i), GetH(i),
134 Player.GetX, Player.GetY, Player.GetW, Player.GetH
135 );
136 end;
138 procedure Die(i : integer);
139 var
140 x, y : integer;
141 begin
142 x := mx[i] + GetW(i) / 2;
143 y := my[i] + GetH(i) / 2;
144 if mtype[i] = zomby then begin
145 Drop.Create(Items.rottenMeat, x, y, Random(3));
146 end;
147 mtype[i] := none;
148 end;
150 procedure Hit(i, damage : integer);
151 begin
152 mhp[i] := mhp[i] - damage;
153 if mhp[i] <= 0 then Die(i);
154 end;
156 (** ZombyThink -- мозги зомби **)
157 (** ma = Кадр анимации **)
158 (** mb = Таймер действий **)
159 (** mc = Состояние **)
161 procedure ZombyThink(i : integer);
162 const
163 stay = 0;
164 walkleft = 1;
165 walkright = 2;
166 angry = 3;
167 const
168 bite = 1;
169 speed = 1;
170 walkTime = 30;
171 angryTime = 200;
172 stayTime = 300;
173 begin
174 // Debug('state: ' + mc[i] + ' / anim ' + ma[i] + ' / ticks ' + mb[i]);
176 mb[i] := mb[i] - 1;
177 if mb[i] < 0 then mb[i] := 0;
178 if CollisionWithPlayer(i) then Player.BiteIt(bite, mvx[i]);
180 (* Вижу игрока - сразу агрюсь и бегу за ним некоторое время *)
181 if (mpos[i] = 0) and (Player.GetX - mx[i] < 0) or (mpos[i] = 1) and (Player.GetX - mx[i] > 0) then
182 if Phy.RayTraced(mx[i], my[i], Player.GetX, Player.GetY) then
183 begin
184 mb[i] := angryTime;
185 mc[i] := angry;
186 end;
188 if mc[i] = stay then begin
189 ma[i] := 0;
190 Swim(i, 0);
191 if (mb[i] <= 0) and (Random(100) <= 2) then
192 begin
193 mb[i] := walkTime + Random(walkTime);
194 mc[i] := walkleft + Random(2); (* Случайный разворот *)
195 end;
196 end else if mc[i] = walkleft then begin
197 if IsSolidStep(i, -speed) then Jump(i, -speed);
198 Step(i, -speed);
199 ma[i] := (ma[i] + 1) mod lastZombyFrame;
200 if mb[i] <= 0 then mc[i] := stay;
201 end else if mc[i] = walkright then begin
202 if IsSolidStep(i, speed) then Jump(i, speed);
203 Step(i, speed);
204 ma[i] := (ma[i] + 1) mod lastZombyFrame;
205 if mb[i] <= 0 then mc[i] := stay;
206 end else if mc[i] = angry then begin
207 if mb[i] <= 0 then begin
208 if Phy.RayTraced(mx[i], my[i], Player.GetX, Player.GetY) then begin
209 (* Видижу игрока - устанавливаю время преследования *)
210 mb[i] := angryTime;
211 end else begin
212 (* Игрока давно не было видно - ждём его некоторое время на месте *)
213 mb[i] := stayTime;
214 mc[i] := stay;
215 end;
216 end else begin
217 (* Бежим за игроком *)
218 if Player.GetX - mx[i] < 4 then begin
219 if IsSolidStep(i, -speed) then Jump(i, -speed);
220 Step(i, -speed);
221 ma[i] := (ma[i] + 1) mod lastZombyFrame;
222 end else if Player.GetX - mx[i] > 4 then begin
223 if IsSolidStep(i, speed) then Jump(i, speed);
224 Step(i, speed);
225 ma[i] := (ma[i] + 1) mod lastZombyFrame;
226 end else begin
227 Swim(i, 0);
228 end;
229 end;
230 end;
231 end;
233 procedure ZombyDraw(i, camx, camy : integer);
234 var
235 frame : integer;
236 begin
237 frame := zombyAnim[ma[i]];
238 DrawImage(zombyBody[mpos[i]], mx[i] - 6 - camx, my[i] - camy);
239 DrawImage(zombyLegs[mpos[i], frame], mx[i] - 2 - camx, my[i] + 20 - camy);
240 end;
242 (* ============= Public ============= *)
244 procedure Create(typ, x, y : integer);
245 var
246 i : integer;
247 begin
248 Assert((typ >= 0) and (typ <= lastType));
249 Debug('Create mob ' + typ +' @ ' + x + 'x' + y);
250 for i := 0 to lastMob do if mtype[i] = none then
251 begin
252 mtype[i] := typ;
253 mx[i] := x;
254 my[i] := y;
255 mvx[i] := 0;
256 mvy[i] := 0;
257 mpos[i] := 0;
258 mhp[i] := thp[typ];
259 mjump[i] := false;
260 ma[i] := 0;
261 mb[i] := 0;
262 mc[i] := 0;
263 debug('Created mob is ' + i);
264 exit;
265 end;
266 Debug('Mob type ' + typ + ' not created');
267 end;
269 function FindAndHit(damage, x, y, w, h, addvx, addvy : integer) : integer;
270 var
271 i : integer;
272 begin
273 for i := 0 to lastMob do if mtype[i] <> none then
274 if Phy.IntersectRects(x, y, w, h, mx[i], my[i], GetW(i), GetH(i)) then
275 begin
276 mvx[i] := mvx[i] + addvx;
277 mvy[i] := mvy[i] + addvy;
278 Hit(i, damage);
279 FindAndHit := i;
280 exit;
281 end;
282 FindAndHit := -1;
283 end;
285 procedure Update;
286 var
287 i, typ : integer;
288 begin
289 for i := 0 to lastMob do if mtype[i] <> none then
290 begin
291 typ := mtype[i];
292 if typ = zomby then ZombyThink(i);
293 if mhp[i] <= 0 then Die(i);
294 end;
295 end;
297 procedure UpdatePhy;
298 var
299 i : integer;
300 begin
301 for i := 0 to lastMob do
302 begin
303 UseObject(i);
304 Phy.Step(true);
305 UpdateObject(i);
306 end;
307 end;
309 procedure Draw(camx, camy:integer);
310 var
311 i, typ : integer;
312 begin
313 for i := 0 to lastMob do if mtype[i] <> none then
314 begin
315 typ := mtype[i];
316 if typ = zomby then ZombyDraw(i, camx, camy);
317 //SetColor(0, 0, 255);
318 //DrawRect(mx[i] - camx, my[i] - camy, GetW(i) - 1, GetH(i) - 1);
319 end;
320 end;
322 procedure LoadTextures(path : string);
323 var
324 im : image;
325 begin
326 im := ld_tex('zombie_ani.png', path, 'mobs/');
327 zombyBody[0] := rotate_image_from_image(im, 0, 0, 20, 22, 0);
328 zombyBody[1] := rotate_image_from_image(im, 21, 0, 20, 22, 0);
329 zombyLegs[0, 0] := rotate_image_from_image(im, 0, 52, 12, 12, 0);
330 zombyLegs[0, 1] := rotate_image_from_image(im, 13, 52, 12, 12, 0);
331 zombyLegs[0, 2] := rotate_image_from_image(im, 26, 52, 12, 12, 0);
332 zombyLegs[1, 0] := rotate_image_from_image(im, 39, 52, 12, 12, 0);
333 zombyLegs[1, 1] := rotate_image_from_image(im, 52, 52, 12, 12, 0);
334 zombyLegs[1, 2] := rotate_image_from_image(im, 65, 52, 12, 12, 0);
335 end;
337 procedure FreeTextures;
338 var
339 i, j : integer;
340 nullimg : image;
341 begin
342 for i := 0 to 1 do
343 begin
344 zombyBody[i] := nullimg;
345 for j := 0 to 2 do zombyLegs[i, j] := nullimg;
346 end;
347 end;
349 procedure SaveData;
350 var
351 i : integer;
352 begin
353 for i := 0 to lastMob do
354 begin
355 write_byte(mtype[i]);
356 writeint(mx[i]);
357 writeint(my[i]);
358 writeint(mvx[i]);
359 writeint(mvy[i]);
360 write_byte(mpos[i]);
361 writeint(mhp[i]);
362 writebool(mjump[i]);
363 writeint(ma[i]);
364 writeint(mb[i]);
365 writeint(mc[i]);
366 end;
367 end;
369 procedure LoadData;
370 var
371 i : integer;
372 begin
373 for i := 0 to lastMob do
374 begin
375 mtype[i] := read_byte;
376 mx[i] := readint;
377 my[i] := readint;
378 mvx[i] := readint;
379 mvy[i] := readint;
380 mpos[i] := read_byte;
381 mhp[i] := readint;
382 mjump[i] := readbool;
383 ma[i] := readint;
384 mb[i] := readint;
385 mc[i] := readint;
386 end;
387 end;
389 procedure ResetData;
390 var
391 i : integer;
392 begin
393 for i := 0 to lastMob do
394 begin
395 mtype[i] := none;
396 mx[i] := 0;
397 my[i] := 0;
398 mvx[i] := 0;
399 mvy[i] := 0;
400 mpos[i] := 0;
401 mhp[i] := 0;
402 mjump[i] := false;
403 ma[i] := 0;
404 mb[i] := 0;
405 mc[i] := 0;
406 end;
407 end;
409 initialization
410 InitTab(none, 0, 0, 0, 0);
411 InitTab(zomby, 8, 32, 10, 7);
413 zombyAnim[0] := 0;
414 zombyAnim[1] := 0;
415 zombyAnim[2] := 0;
416 zombyAnim[3] := 1;
417 zombyAnim[4] := 1;
418 zombyAnim[5] := 1;
419 zombyAnim[6] := 2;
420 zombyAnim[7] := 2;
421 zombyAnim[8] := 2;
422 zombyAnim[9] := 1;
423 zombyAnim[10] := 1;
424 zombyAnim[11] := 1;
425 end.