DEADSOFTWARE

game: execute onwadend event after state is changed to INTERCUSTOM
[d2df-sdl.git] / src / game / g_basic.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 unit g_basic;
18 interface
20 uses
21 utils, g_phys;
23 const
24 GAME_VERSION = '0.667';
25 GAME_BUILDDATE = {$I %DATE%};
26 GAME_BUILDTIME = {$I %TIME%};
27 UID_GAME = 1;
28 UID_PLAYER = 2;
29 UID_MONSTER = 3;
30 UID_ITEM = 10;
31 UID_MAX_GAME = $10;
32 UID_MAX_PLAYER = $7FFF;
33 UID_MAX_MONSTER = $FFFF;
35 type
36 TDirection = (D_LEFT, D_RIGHT);
37 WArray = array of Word;
38 DWArray = array of DWORD;
39 String20 = String[20];
41 function g_GetBuilderName (): AnsiString;
42 function g_GetBuildHash (full: Boolean = True): AnsiString;
44 function g_CreateUID(UIDType: Byte): Word;
45 function g_GetUIDType(UID: Word): Byte;
46 function g_Collide(X1, Y1: Integer; Width1, Height1: Word;
47 X2, Y2: Integer; Width2, Height2: Word): Boolean; inline;
48 function g_CollideLine(x1, y1, x2, y2, rX, rY: Integer; rWidth, rHeight: Word): Boolean;
49 function g_CollidePoint(X, Y, X2, Y2: Integer; Width, Height: Word): Boolean; inline;
50 function g_CollideLevel(X, Y: Integer; Width, Height: Word): Boolean; inline;
51 function g_CollideAround(X1, Y1: Integer; Width1, Height1: Word;
52 X2, Y2: Integer; Width2, Height2: Word): Boolean; inline;
53 function g_CollidePlayer(X, Y: Integer; Width, Height: Word): Boolean; inline;
54 function g_PatchLength(X1, Y1, X2, Y2: Integer): Word;
55 function g_TraceVector(X1, Y1, X2, Y2: Integer): Boolean; // `true`: no wall hit
56 function g_GetAcidHit(X, Y: Integer; Width, Height: Word): Byte;
57 function g_Look(a, b: PObj; d: TDirection): Boolean;
58 procedure IncMax(var A: Integer; B, Max: Integer); overload;
59 procedure IncMax(var A: Single; B, Max: Single); overload;
60 procedure IncMax(var A: Integer; Max: Integer); overload;
61 procedure IncMax(var A: Single; Max: Single); overload;
62 procedure IncMax(var A: Word; B, Max: Word); overload;
63 procedure IncMax(var A: Word; Max: Word); overload;
64 procedure IncMax(var A: SmallInt; B, Max: SmallInt); overload;
65 procedure IncMax(var A: SmallInt; Max: SmallInt); overload;
66 procedure DecMin(var A: Integer; B, Min: Integer); overload;
67 procedure DecMin(var A: Single; B, Min: Single); overload;
68 procedure DecMin(var A: Integer; Min: Integer); overload;
69 procedure DecMin(var A: Single; Min: Single); overload;
70 procedure DecMin(var A: Word; B, Min: Word); overload;
71 procedure DecMin(var A: Word; Min: Word); overload;
72 procedure DecMin(var A: Byte; B, Min: Byte); overload;
73 procedure DecMin(var A: Byte; Min: Byte); overload;
74 function Sign(A: Integer): ShortInt; overload;
75 function Sign(A: Single): ShortInt; overload;
76 function PointToRect(X, Y, X1, Y1: Integer; Width, Height: Word): Integer;
77 function GetAngle(baseX, baseY, pointX, PointY: Integer): SmallInt;
78 function GetAngle2(vx, vy: Integer): SmallInt;
79 function GetLines(Text: string; FontID: DWORD; MaxWidth: Word): SSArray;
80 procedure Sort(var a: SSArray);
81 function Sscanf(const s: string; const fmt: string;
82 const Pointers: array of Pointer): Integer;
83 function InDWArray(a: DWORD; arr: DWArray): Boolean;
84 function InWArray(a: Word; arr: WArray): Boolean;
85 function InSArray(a: string; arr: SSArray): Boolean;
86 function GetPos(UID: Word; o: PObj): Boolean;
87 function parse(s: string): SSArray;
88 function parse2(s: string; delim: Char): SSArray;
89 function g_GetFileTime(fileName: String): Integer;
90 function g_SetFileTime(fileName: String; time: Integer): Boolean;
91 procedure SortSArray(var S: SSArray);
92 function b_Text_Format(S: string): string;
93 function b_Text_Unformat(S: string): string;
94 function b_Text_Wrap(S: string; LineLen: Integer): string;
95 function b_Text_LineCount(S: string): Integer;
97 var
98 gmon_dbg_los_enabled: Boolean = true;
100 implementation
102 uses
103 Math, geom, e_log, g_map, g_gfx, g_player, SysUtils, MAPDEF,
104 StrUtils, e_graphics, g_monsters, g_items, g_game;
106 {$PUSH}
107 {$WARN 2054 OFF} // unknwon env var
108 {$WARN 6018 OFF} // unreachable code
109 function g_GetBuilderName (): AnsiString;
110 begin
111 if {$I %D2DF_BUILD_USER%} <> '' then
112 result := {$I %D2DF_BUILD_USER%} // custom
113 else if {$I %USER%} <> '' then
114 result := {$I %USER%} // unix username
115 else if {$I %USERNAME%} <> '' then
116 result := {$I %USERNAME%} // windows username
117 else
118 result := 'unknown'
119 end;
121 function g_GetBuildHash (full: Boolean = True): AnsiString;
122 begin
123 if {$I %D2DF_BUILD_HASH%} <> '' then
124 if full then
125 result := {$I %D2DF_BUILD_HASH%}
126 else
127 result := Copy({$I %D2DF_BUILD_HASH%}, 1, 7)
128 else
129 result := 'custom build'
130 end;
131 {$POP}
133 function g_PatchLength(X1, Y1, X2, Y2: Integer): Word;
134 begin
135 Result := Min(Round(Hypot(Abs(X2-X1), Abs(Y2-Y1))), 65535);
136 end;
138 function g_CollideLevel(X, Y: Integer; Width, Height: Word): Boolean; inline;
139 begin
140 result := g_Map_CollidePanel(X, Y, Width, Height, (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR), false);
141 end;
142 (*
143 var
144 a: Integer;
145 begin
146 Result := False;
148 if gWalls = nil then
149 Exit;
151 for a := 0 to High(gWalls) do
152 if gWalls[a].Enabled and
153 not ( ((Y + Height <= gWalls[a].Y) or
154 (Y >= gWalls[a].Y + gWalls[a].Height)) or
155 ((X + Width <= gWalls[a].X) or
156 (X >= gWalls[a].X + gWalls[a].Width)) ) then
157 begin
158 Result := True;
159 Exit;
160 end;
161 end;
162 *)
164 function g_CollidePlayer(X, Y: Integer; Width, Height: Word): Boolean; inline;
165 var
166 a: Integer;
167 begin
168 Result := False;
170 if gPlayers = nil then Exit;
172 for a := 0 to High(gPlayers) do
173 if (gPlayers[a] <> nil) and gPlayers[a].alive then
174 if gPlayers[a].Collide(X, Y, Width, Height) then
175 begin
176 Result := True;
177 Exit;
178 end;
179 end;
182 function g_TraceVector(X1, Y1, X2, Y2: Integer): Boolean;
183 var
184 wallHitX: Integer = 0;
185 wallHitY: Integer = 0;
186 (*
187 i: Integer;
188 dx, dy: Integer;
189 Xerr, Yerr, d: LongWord;
190 incX, incY: Integer;
191 x, y: Integer;
192 *)
193 begin
194 (*
195 result := False;
197 Assert(gCollideMap <> nil, 'g_TraceVector: gCollideMap = nil');
199 Xerr := 0;
200 Yerr := 0;
201 dx := X2-X1;
202 dy := Y2-Y1;
204 if dx > 0 then incX := 1 else if dx < 0 then incX := -1 else incX := 0;
205 if dy > 0 then incY := 1 else if dy < 0 then incY := -1 else incY := 0;
207 dx := abs(dx);
208 dy := abs(dy);
210 if dx > dy then d := dx else d := dy;
212 x := X1;
213 y := Y1;
215 for i := 1 to d do
216 begin
217 Inc(Xerr, dx);
218 Inc(Yerr, dy);
219 if Xerr>d then
220 begin
221 Dec(Xerr, d);
222 Inc(x, incX);
223 end;
224 if Yerr > d then
225 begin
226 Dec(Yerr, d);
227 Inc(y, incY);
228 end;
230 if (y > gMapInfo.Height-1) or
231 (y < 0) or (x > gMapInfo.Width-1) or (x < 0) then
232 Exit;
233 if ByteBool(gCollideMap[y, x] and MARK_BLOCKED) then
234 Exit;
235 end;
237 Result := True;
238 *)
240 // `true` if no obstacles
241 if (g_profile_los) then g_Mons_LOS_Start();
242 result := (g_Map_traceToNearestWall(x1, y1, x2, y2, @wallHitX, @wallHitY) = nil);
243 if (g_profile_los) then g_Mons_LOS_End();
244 end;
247 function g_CreateUID(UIDType: Byte): Word;
248 var
249 ok: Boolean;
250 i: Integer;
251 begin
252 Result := $0;
254 case UIDType of
255 UID_PLAYER:
256 begin
257 repeat
258 Result := UID_MAX_GAME+$1+Random(UID_MAX_PLAYER-UID_MAX_GAME+$1);
260 ok := True;
261 if gPlayers <> nil then
262 for i := 0 to High(gPlayers) do
263 if gPlayers[i] <> nil then
264 if Result = gPlayers[i].UID then
265 begin
266 ok := False;
267 Break;
268 end;
269 until ok;
270 end;
272 UID_MONSTER:
273 begin
274 //FIXME!!!
275 while true do
276 begin
277 result := UID_MAX_PLAYER+$1+Random(UID_MAX_MONSTER-UID_MAX_GAME-UID_MAX_PLAYER+$1);
278 if (g_Monsters_ByUID(result) = nil) then break;
279 end;
280 end;
281 end;
282 end;
284 function g_GetUIDType(UID: Word): Byte;
285 begin
286 if UID <= UID_MAX_GAME then
287 Result := UID_GAME
288 else
289 if UID <= UID_MAX_PLAYER then
290 Result := UID_PLAYER
291 else
292 Result := UID_MONSTER;
293 end;
295 function g_Collide(X1, Y1: Integer; Width1, Height1: Word;
296 X2, Y2: Integer; Width2, Height2: Word): Boolean; inline;
297 begin
298 Result := not ( ((Y1 + Height1 <= Y2) or
299 (Y2 + Height2 <= Y1)) or
300 ((X1 + Width1 <= X2) or
301 (X2 + Width2 <= X1)) );
302 end;
304 function g_CollideAround(X1, Y1: Integer; Width1, Height1: Word;
305 X2, Y2: Integer; Width2, Height2: Word): Boolean; inline;
306 begin
307 Result := g_Collide(X1, Y1, Width1, Height1, X2, Y2, Width2, Height2) or
308 g_Collide(X1+1, Y1, Width1, Height1, X2, Y2, Width2, Height2) or
309 g_Collide(X1-1, Y1, Width1, Height1, X2, Y2, Width2, Height2) or
310 g_Collide(X1, Y1+1, Width1, Height1, X2, Y2, Width2, Height2) or
311 g_Collide(X1, Y1-1, Width1, Height1, X2, Y2, Width2, Height2);
312 end;
314 function c(X1, Y1, Width1, Height1, X2, Y2, Width2, Height2: Integer): Boolean; inline;
315 begin
316 Result := not (((Y1 + Height1 <= Y2) or
317 (Y1 >= Y2 + Height2)) or
318 ((X1 + Width1 <= X2) or
319 (X1 >= X2 + Width2)));
320 end;
322 function g_Collide2(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer): Boolean; inline;
323 begin
324 //Result := not (((Y2 <= Y3) or (Y1 >= Y4)) or ((X2 <= X3) or (X1 >= X4)));
325 Result := c(X1, Y1, X2-X1, Y2-Y1, X3, Y3, X4-X3, Y4-Y3);
326 end;
328 function g_CollidePoint(X, Y, X2, Y2: Integer; Width, Height: Word): Boolean; inline;
329 begin
330 X := X-X2;
331 Y := Y-Y2;
332 Result := (x >= 0) and (x <= Width) and
333 (y >= 0) and (y <= Height);
334 end;
336 procedure IncMax(var A: Integer; B, Max: Integer);
337 begin
338 if A+B > Max then A := Max else A := A+B;
339 end;
341 procedure IncMax(var A: Single; B, Max: Single);
342 begin
343 if A+B > Max then A := Max else A := A+B;
344 end;
346 procedure DecMin(var A: Integer; B, Min: Integer);
347 begin
348 if A-B < Min then A := Min else A := A-B;
349 end;
351 procedure DecMin(var A: Word; B, Min: Word);
352 begin
353 if A-B < Min then A := Min else A := A-B;
354 end;
356 procedure DecMin(var A: Single; B, Min: Single);
357 begin
358 if A-B < Min then A := Min else A := A-B;
359 end;
361 procedure IncMax(var A: Integer; Max: Integer);
362 begin
363 if A+1 > Max then A := Max else A := A+1;
364 end;
366 procedure IncMax(var A: Single; Max: Single);
367 begin
368 if A+1 > Max then A := Max else A := A+1;
369 end;
371 procedure IncMax(var A: Word; B, Max: Word);
372 begin
373 if A+B > Max then A := Max else A := A+B;
374 end;
376 procedure IncMax(var A: Word; Max: Word);
377 begin
378 if A+1 > Max then A := Max else A := A+1;
379 end;
381 procedure IncMax(var A: SmallInt; B, Max: SmallInt);
382 begin
383 if A+B > Max then A := Max else A := A+B;
384 end;
386 procedure IncMax(var A: SmallInt; Max: SmallInt);
387 begin
388 if A+1 > Max then A := Max else A := A+1;
389 end;
391 procedure DecMin(var A: Integer; Min: Integer);
392 begin
393 if A-1 < Min then A := Min else A := A-1;
394 end;
396 procedure DecMin(var A: Single; Min: Single);
397 begin
398 if A-1 < Min then A := Min else A := A-1;
399 end;
401 procedure DecMin(var A: Word; Min: Word);
402 begin
403 if A-1 < Min then A := Min else A := A-1;
404 end;
406 procedure DecMin(var A: Byte; B, Min: Byte);
407 begin
408 if A-B < Min then A := Min else A := A-B;
409 end;
411 procedure DecMin(var A: Byte; Min: Byte); overload;
412 begin
413 if A-1 < Min then A := Min else A := A-1;
414 end;
416 function Sign(A: Integer): ShortInt;
417 begin
418 if A < 0 then Result := -1
419 else if A > 0 then Result := 1
420 else Result := 0;
421 end;
423 function Sign(A: Single): ShortInt;
424 const
425 Eps = 1.0E-5;
426 begin
427 if Abs(A) < Eps then Result := 0
428 else if A < 0 then Result := -1
429 else Result := 1;
430 end;
432 function PointToRect(X, Y, X1, Y1: Integer; Width, Height: Word): Integer;
433 begin
434 X := X-X1; // A(0;0) --- B(W;0)
435 Y := Y-Y1; // | |
436 // D(0;H) --- C(W;H)
437 if X < 0 then
438 begin // Ñëåâà
439 if Y < 0 then // Ñëåâà ñâåðõó: ðàññòîÿíèå äî A
440 Result := Round(Hypot(X, Y))
441 else
442 if Y > Height then // Ñëåâà ñíèçó: ðàññòîÿíèå äî D
443 Result := Round(Hypot(X, Y-Height))
444 else // Ñëåâà ïîñåðåäèíå: ðàññòîÿíèå äî AD
445 Result := -X;
446 end
447 else
448 if X > Width then
449 begin // Ñïðàâà
450 X := X-Width;
451 if y < 0 then // Ñïðàâà ñâåðõó: ðàññòîÿíèå äî B
452 Result := Round(Hypot(X, Y))
453 else
454 if Y > Height then // Ñïðàâà ñíèçó: ðàññòîÿíèå äî C
455 Result := Round(Hypot(X, Y-Height))
456 else // Ñïðàâà ïîñåðåäèíå: ðàññòîÿíèå äî BC
457 Result := X;
458 end
459 else // Ïîñåðåäèíå
460 begin
461 if Y < 0 then // Ïîñåðåäèíå ñâåðõó: ðàññòîÿíèå äî AB
462 Result := -Y
463 else
464 if Y > Height then // Ïîñåðåäèíå ñíèçó: ðàññòîÿíèå äî DC
465 Result := Y-Height
466 else // Âíóòðè ïðÿìîóãîëüíèêà: ðàññòîÿíèå 0
467 Result := 0;
468 end;
469 end;
471 function g_GetAcidHit(X, Y: Integer; Width, Height: Word): Byte;
472 const
473 tab: array[0..3] of Byte = (0, 5, 10, 20);
474 var
475 a: Byte;
476 begin
477 a := 0;
479 if g_Map_CollidePanel(X, Y, Width, Height, PANEL_ACID1, False) then a := a or 1;
480 if g_Map_CollidePanel(X, Y, Width, Height, PANEL_ACID2, False) then a := a or 2;
482 Result := tab[a];
483 end;
485 function g_Look(a, b: PObj; d: TDirection): Boolean;
486 begin
487 if not gmon_dbg_los_enabled then begin result := false; exit; end; // always "wall hit"
489 if ((b^.X > a^.X) and (d = TDirection.D_LEFT)) or
490 ((b^.X < a^.X) and (d = TDirection.D_RIGHT)) then
491 begin
492 Result := False;
493 Exit;
494 end;
496 Result := g_TraceVector(a^.X+a^.Rect.X+(a^.Rect.Width div 2),
497 a^.Y+a^.Rect.Y+(a^.Rect.Height div 2),
498 b^.X+b^.Rect.X+(b^.Rect.Width div 2),
499 b^.Y+b^.Rect.Y+(b^.Rect.Height div 2));
500 end;
502 function GetAngle(baseX, baseY, pointX, PointY: Integer): SmallInt;
503 var
504 c: Single;
505 a, b: Integer;
506 begin
507 a := abs(pointX-baseX);
508 b := abs(pointY-baseY);
510 if a = 0 then c := 90
511 else c := RadToDeg(ArcTan(b/a));
513 if pointY < baseY then c := -c;
514 if pointX > baseX then c := 180-c;
516 Result := Round(c);
517 end;
519 function GetAngle2(vx, vy: Integer): SmallInt;
520 var
521 c: Single;
522 a, b: Integer;
523 begin
524 a := abs(vx);
525 b := abs(vy);
527 if a = 0 then
528 c := 90
529 else
530 c := RadToDeg(ArcTan(b/a));
532 if vy < 0 then
533 c := -c;
534 if vx > 0 then
535 c := 180 - c;
537 c := c + 180;
539 Result := Round(c);
540 end;
542 {function g_CollideLine(x1, y1, x2, y2, rX, rY: Integer; rWidth, rHeight: Word): Boolean;
543 const
544 table: array[0..8, 0..8] of Byte =
545 ((0, 0, 3, 3, 1, 2, 2, 0, 1),
546 (0, 0, 0, 0, 4, 7, 2, 0, 1),
547 (3, 0, 0, 0, 4, 4, 1, 3, 1),
548 (3, 0, 0, 0, 0, 0, 5, 6, 1),
549 (1, 4, 4, 0, 0, 0, 5, 5, 1),
550 (2, 7, 4, 0, 0, 0, 0, 0, 1),
551 (2, 2, 1, 5, 5, 0, 0, 0, 1),
552 (0, 0, 3, 6, 5, 0, 0, 0, 1),
553 (1, 1, 1, 1, 1, 1, 1, 1, 1));
555 function GetClass(x, y: Integer): Byte;
556 begin
557 if y < rY then
558 begin
559 if x < rX then Result := 7
560 else if x < rX+rWidth then Result := 0
561 else Result := 1;
562 end
563 else if y < rY+rHeight then
564 begin
565 if x < rX then Result := 6
566 else if x < rX+rWidth then Result := 8
567 else Result := 2;
568 end
569 else
570 begin
571 if x < rX then Result := 5
572 else if x < rX+rWidth then Result := 4
573 else Result := 3;
574 end;
575 end;
577 begin
578 case table[GetClass(x1, y1), GetClass(x2, y2)] of
579 0: Result := False;
580 1: Result := True;
581 2: Result := Abs((rY-y1))/Abs((rX-x1)) <= Abs((y2-y1))/Abs((x2-x1));
582 3: Result := Abs((rY-y1))/Abs((rX+rWidth-x1)) <= Abs((y2-y1))/Abs((x2-x1));
583 4: Result := Abs((rY+rHeight-y1))/Abs((rX+rWidth-x1)) >= Abs((y2-y1))/Abs((x2-x1));
584 5: Result := Abs((rY+rHeight-y1))/Abs((rX-x1)) >= Abs((y2-y1))/Abs((x2-x1));
585 6: Result := (Abs((rY-y1))/Abs((rX+rWidth-x1)) <= Abs((y2-y1))/Abs((x2-x1))) and
586 (Abs((rY+rHeight-y1))/Abs((rX-x1)) >= Abs((y2-y1))/Abs((x2-x1)));
587 7: Result := (Abs((rY+rHeight-y1))/Abs((rX+rWidth-x1)) >= Abs((y2-y1))/Abs((x2-x1))) and
588 (Abs((rY-y1))/Abs((rX-x1)) <= Abs((y2-y1))/Abs((x2-x1)));
589 else Result := False;
590 end;
591 end;}
593 function g_CollideLine(x1, y1, x2, y2, rX, rY: Integer; rWidth, rHeight: Word): Boolean;
595 var
596 i: Integer;
597 dx, dy: Integer;
598 Xerr, Yerr: Integer;
599 incX, incY: Integer;
600 x, y, d: Integer;
602 begin
603 result := lineAABBIntersects(x1, y1, x2, y2, rX, rY, rWidth, rHeight);
605 Result := True;
607 Xerr := 0;
608 Yerr := 0;
609 dx := X2-X1;
610 dy := Y2-Y1;
612 if dx > 0 then incX := 1 else if dx < 0 then incX := -1 else incX := 0;
613 if dy > 0 then incY := 1 else if dy < 0 then incY := -1 else incY := 0;
615 dx := abs(dx);
616 dy := abs(dy);
618 if dx > dy then d := dx else d := dy;
620 x := X1;
621 y := Y1;
623 for i := 1 to d+1 do
624 begin
625 Inc(Xerr, dx);
626 Inc(Yerr, dy);
627 if Xerr > d then
628 begin
629 Dec(Xerr, d);
630 Inc(x, incX);
631 end;
632 if Yerr > d then
633 begin
634 Dec(Yerr, d);
635 Inc(y, incY);
636 end;
638 if (x >= rX) and (x <= (rX + rWidth - 1)) and
639 (y >= rY) and (y <= (rY + rHeight - 1)) then Exit;
640 end;
642 Result := False;
644 end;
646 function GetStr(var Str: string): string;
647 var
648 a: Integer;
649 begin
650 Result := '';
651 for a := 1 to Length(Str) do
652 if (a = Length(Str)) or (Str[a+1] = ' ') then
653 begin
654 Result := Copy(Str, 1, a);
655 Delete(Str, 1, a+1);
656 Str := Trim(Str);
657 Exit;
658 end;
659 end;
661 function GetLines (Text: string; FontID: DWORD; MaxWidth: Word): SSArray;
662 var i, j, len, lines: Integer;
664 function GetLine (j, i: Integer): String;
665 begin
666 result := Copy(text, j, i - j + 1);
667 end;
669 function GetWidth (j, i: Integer): Integer;
670 var w, h: Word;
671 begin
672 e_CharFont_GetSize(FontID, GetLine(j, i), w, h);
673 result := w
674 end;
676 begin
677 result := nil; lines := 0;
678 j := 1; i := 1; len := Length(Text);
679 // e_LogWritefln('GetLines @%s len=%s [%s]', [MaxWidth, len, Text]);
680 while j <= len do
681 begin
682 (* --- Get longest possible sequence --- *)
683 while (i + 1 <= len) and (GetWidth(j, i + 1) <= MaxWidth) do Inc(i);
684 (* --- Do not include part of word --- *)
685 if (i < len) and (text[i] <> ' ') then
686 while (i >= j) and (text[i] <> ' ') do Dec(i);
687 (* --- Do not include spaces --- *)
688 while (i >= j) and (text[i] = ' ') do Dec(i);
689 (* --- Add line --- *)
690 SetLength(result, lines + 1);
691 result[lines] := GetLine(j, i);
692 // e_LogWritefln(' -> (%s:%s::%s) [%s]', [j, i, GetWidth(j, i), result[lines]]);
693 Inc(lines);
694 (* --- Skip spaces --- *)
695 while (i <= len) and (text[i] = ' ') do Inc(i);
696 j := i + 2;
697 end;
698 end;
700 procedure Sort(var a: SSArray);
701 var
702 i, j: Integer;
703 s: string;
704 begin
705 if a = nil then Exit;
707 for i := High(a) downto Low(a) do
708 for j := Low(a) to High(a)-1 do
709 if LowerCase(a[j]) > LowerCase(a[j+1]) then
710 begin
711 s := a[j];
712 a[j] := a[j+1];
713 a[j+1] := s;
714 end;
715 end;
717 function Sscanf(const s: String; const fmt: String;
718 const Pointers: array of Pointer): Integer;
719 var
720 i, j, n, m: Integer;
721 s1: ShortString;
722 L: LongInt;
723 X: Extended;
725 function GetInt(): Integer;
726 begin
727 s1 := '';
728 while (n <= Length(s)) and (s[n] = ' ') do
729 Inc(n);
731 while (n <= Length(s)) and (s[n] in ['0'..'9', '+', '-']) do
732 begin
733 s1 := s1 + s[n];
734 Inc(n);
735 end;
737 Result := Length(s1);
738 end;
740 function GetFloat(): Integer;
741 begin
742 s1 := '';
743 while (n <= Length(s)) and (s[n] = ' ') do
744 Inc(n);
746 while (n <= Length(s)) and //jd >= rather than >
747 (s[n] in ['0'..'9', '+', '-', '.', 'e', 'E']) do
748 begin
749 s1 := s1 + s[n];
750 Inc(n);
751 end;
753 Result := Length(s1);
754 end;
756 function GetString(): Integer;
757 begin
758 s1 := '';
759 while (n <= Length(s)) and (s[n] = ' ') do
760 Inc(n);
762 while (n <= Length(s)) and (s[n] <> ' ') do
763 begin
764 s1 := s1 + s[n];
765 Inc(n);
766 end;
768 Result := Length(s1);
769 end;
771 function ScanStr(c: Char): Boolean;
772 begin
773 while (n <= Length(s)) and (s[n] <> c) do
774 Inc(n);
775 Inc(n);
777 Result := (n <= Length(s));
778 end;
780 function GetFmt(): Integer;
781 begin
782 Result := -1;
784 while (True) do
785 begin
786 while (fmt[m] = ' ') and (m < Length(fmt)) do
787 Inc(m);
788 if (m >= Length(fmt)) then
789 Break;
791 if (fmt[m] = '%') then
792 begin
793 Inc(m);
794 case fmt[m] of
795 'd': Result := vtInteger;
796 'f': Result := vtExtended;
797 's': Result := vtString;
798 end;
799 Inc(m);
800 Break;
801 end;
803 if (not ScanStr(fmt[m])) then
804 Break;
805 Inc(m);
806 end;
807 end;
809 begin
810 n := 1;
811 m := 1;
812 Result := 0;
813 s1 := '';
815 for i := 0 to High(Pointers) do
816 begin
817 j := GetFmt();
819 case j of
820 vtInteger :
821 begin
822 if GetInt() > 0 then
823 begin
824 L := StrToIntDef(s1, 0);
825 Move(L, Pointers[i]^, SizeOf(LongInt));
826 Inc(Result);
827 end
828 else
829 Break;
830 end;
832 vtExtended :
833 begin
834 if GetFloat() > 0 then
835 begin
836 X := StrToFloatDef(s1, 0.0);
837 Move(X, Pointers[i]^, SizeOf(Extended));
838 Inc(Result);
839 end
840 else
841 Break;
842 end;
844 vtString :
845 begin
846 if GetString() > 0 then
847 begin
848 Move(s1, Pointers[i]^, Length(s1)+1);
849 Inc(Result);
850 end
851 else
852 Break;
853 end;
855 else {case}
856 Break;
857 end; {case}
858 end;
859 end;
861 function InDWArray(a: DWORD; arr: DWArray): Boolean;
862 var
863 b: Integer;
864 begin
865 Result := False;
867 if arr = nil then Exit;
869 for b := 0 to High(arr) do
870 if arr[b] = a then
871 begin
872 Result := True;
873 Exit;
874 end;
875 end;
877 function InWArray(a: Word; arr: WArray): Boolean;
878 var
879 b: Integer;
880 begin
881 Result := False;
883 if arr = nil then Exit;
885 for b := 0 to High(arr) do
886 if arr[b] = a then
887 begin
888 Result := True;
889 Exit;
890 end;
891 end;
893 function InSArray(a: string; arr: SSArray): Boolean;
894 var
895 b: Integer;
896 begin
897 Result := False;
899 if arr = nil then Exit;
901 a := AnsiLowerCase(a);
903 for b := 0 to High(arr) do
904 if AnsiLowerCase(arr[b]) = a then
905 begin
906 Result := True;
907 Exit;
908 end;
909 end;
911 function GetPos(UID: Word; o: PObj): Boolean;
912 var
913 p: TPlayer;
914 m: TMonster;
915 begin
916 Result := False;
918 case g_GetUIDType(UID) of
919 UID_PLAYER:
920 begin
921 p := g_Player_Get(UID);
922 if p = nil then Exit;
923 if not p.alive then Exit;
925 o^ := p.Obj;
926 end;
928 UID_MONSTER:
929 begin
930 m := g_Monsters_ByUID(UID);
931 if m = nil then Exit;
932 if not m.alive then Exit;
934 o^ := m.Obj;
935 end;
936 else Exit;
937 end;
939 Result := True;
940 end;
942 function parse(s: String): SSArray;
943 var
944 a: Integer;
945 begin
946 Result := nil;
947 if s = '' then
948 Exit;
950 while s <> '' do
951 begin
952 for a := 1 to Length(s) do
953 if (s[a] = ',') or (a = Length(s)) then
954 begin
955 SetLength(Result, Length(Result)+1);
957 if s[a] = ',' then
958 Result[High(Result)] := Copy(s, 1, a-1)
959 else // Êîíåö ñòðîêè
960 Result[High(Result)] := s;
962 Delete(s, 1, a);
963 Break;
964 end;
965 end;
966 end;
968 function parse2(s: string; delim: Char): SSArray;
969 var
970 a: Integer;
971 begin
972 Result := nil;
973 if s = '' then Exit;
975 while s <> '' do
976 begin
977 for a := 1 to Length(s) do
978 if (s[a] = delim) or (a = Length(s)) then
979 begin
980 SetLength(Result, Length(Result)+1);
982 if s[a] = delim then Result[High(Result)] := Copy(s, 1, a-1)
983 else Result[High(Result)] := s;
985 Delete(s, 1, a);
986 Break;
987 end;
988 end;
989 end;
991 function g_GetFileTime(fileName: String): Integer;
992 var
993 F: File;
994 begin
995 if not FileExists(fileName) then
996 begin
997 Result := -1;
998 Exit;
999 end;
1001 AssignFile(F, fileName);
1002 Reset(F);
1003 Result := FileGetDate(TFileRec(F).Handle);
1004 CloseFile(F);
1005 end;
1007 function g_SetFileTime(fileName: String; time: Integer): Boolean;
1008 var
1009 F: File;
1010 begin
1011 if (not FileExists(fileName)) or (time < 0) then
1012 begin
1013 Result := False;
1014 Exit;
1015 end;
1017 AssignFile(F, fileName);
1018 Reset(F);
1019 Result := (FileSetDate(TFileRec(F).Handle, time) = 0);
1020 CloseFile(F);
1021 end;
1023 procedure SortSArray(var S: SSArray);
1024 var
1025 b: Boolean;
1026 i: Integer;
1027 sw: ShortString;
1028 begin
1029 repeat
1030 b := False;
1031 for i := Low(S) to High(S) - 1 do
1032 if S[i] > S[i + 1] then begin
1033 sw := S[i];
1034 S[i] := S[i + 1];
1035 S[i + 1] := sw;
1036 b := True;
1037 end;
1038 until not b;
1039 end;
1041 function b_Text_Format(S: string): string;
1042 var
1043 Spec, Rst: Boolean;
1044 I: Integer;
1045 begin
1046 Result := '';
1047 Spec := False;
1048 Rst := False;
1049 for I := 1 to Length(S) do
1050 begin
1051 if (not Spec) and (S[I] = '\') and (I + 1 <= Length(S)) then
1052 begin
1053 Spec := True;
1054 Rst := True;
1055 continue;
1056 end;
1057 if Spec then
1058 begin
1059 case S[I] of
1060 'n': // line feed
1061 Result := Result + #10;
1062 '0': // black
1063 Result := Result + #1;
1064 '1': // white
1065 Result := Result + #2;
1066 'd': // darker
1067 Result := Result + #3;
1068 'l': // lighter
1069 Result := Result + #4;
1070 'r': // red
1071 Result := Result + #18;
1072 'g': // green
1073 Result := Result + #19;
1074 'b': // blue
1075 Result := Result + #20;
1076 'y': // yellow
1077 Result := Result + #21;
1078 '\': // escape
1079 Result := Result + '\';
1080 else
1081 Result := Result + '\' + S[I];
1082 end;
1083 Spec := False;
1084 end else
1085 Result := Result + S[I];
1086 end;
1087 // reset to white at end
1088 if Rst then Result := Result + #2;
1089 end;
1091 function b_Text_Unformat(S: string): string;
1092 var
1093 Spec: Boolean;
1094 I: Integer;
1095 begin
1096 Result := '';
1097 Spec := False;
1098 for I := 1 to Length(S) do
1099 begin
1100 if S[I] in [#1, #2, #3, #4, #10, #18, #19, #20, #21] then
1101 begin
1102 Spec := False;
1103 continue;
1104 end;
1105 if (not Spec) and (S[I] = '\') and (I + 1 <= Length(S)) then
1106 begin
1107 Spec := True;
1108 continue;
1109 end;
1110 if Spec then
1111 begin
1112 case S[I] of
1113 'n': ;
1114 '0': ;
1115 '1': ;
1116 'd': ;
1117 'l': ;
1118 'r': ;
1119 'g': ;
1120 'b': ;
1121 'y': ;
1122 '\': Result := Result + '\';
1123 else
1124 Result := Result + '\' + S[I];
1125 end;
1126 Spec := False;
1127 end else
1128 Result := Result + S[I];
1129 end;
1130 end;
1132 function b_Text_Wrap(S: string; LineLen: Integer): string;
1133 begin
1134 Result := WrapText(S, ''#10, [#10, ' ', '-'], LineLen);
1135 end;
1137 function b_Text_LineCount(S: string): Integer;
1138 var
1139 I: Integer;
1140 begin
1141 Result := IfThen(S = '', 0, 1);
1142 for I := 1 to High(S) do
1143 if S[I] = #10 then
1144 Inc(Result);
1145 end;
1147 end.