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, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 {.$DEFINE GWEP_HITSCAN_TRACE_BITMAP_CHECKER}
20 interface
22 uses
25 const
38 type
50 procedure positionChanged (); //WARNING! call this after monster position was changed, or coldet will not work right!
54 var
62 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
64 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
66 procedure g_Weapon_gun(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
69 procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
70 procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: Integer = -1; Silent: Boolean = False);
71 procedure g_Weapon_flame(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
72 procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
73 procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
74 procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
75 procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
76 procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
77 procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
78 procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False);
97 const
121 var
125 implementation
127 uses
134 type
141 const
162 type
170 // indicies in `wgunHitTime` array
173 var
182 begin
186 begin
187 // a is monster
190 end
191 else
192 begin
193 // a is player
201 begin
212 begin
223 var
225 begin
229 begin
232 Exit;
236 begin
239 end
240 else
241 begin
249 var
253 begin
255 Exit;
260 begin
272 begin
281 begin
294 begin
300 Break;
311 var
316 var
317 //a, b, c, d, i1, i2: Integer;
318 //chkTrap_pl, chkTrap_mn: WArray;
323 {
324 function monsWaterCheck (mon: TMonster): Boolean;
325 begin
326 result := false; // don't stop
327 if mon.Live and mon.Collide(gWater[WaterMap[a][c]]) and (not InWArray(monidx, chkTrap_mn)) and (i2 < 1023) then //FIXME
328 begin
329 i2 += 1;
330 chkTrap_mn[i2] := monidx;
331 end;
332 end;
333 }
336 begin
339 begin
346 var
349 begin
354 //i1 := -1;
355 //i2 := -1;
357 //SetLength(chkTrap_pl, 1024);
358 //SetLength(chkTrap_mn, 1024);
359 //for d := 0 to 1023 do chkTrap_pl[d] := $FFFF;
360 //for d := 0 to 1023 do chkTrap_mn[d] := $FFFF;
363 begin
365 begin
370 begin
373 begin
375 begin
377 begin
381 begin
390 //g_Mons_ForEach(monsWaterCheck);
399 //chkTrap_pl := nil;
400 //chkTrap_mn := nil;
403 function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
404 var
407 begin
412 begin
416 else
418 end
419 else
424 begin
425 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì:
427 Exit;
428 // Êèáåð äåìîí è áî÷êà âîîáùå íå ìîãóò ñåáÿ ðàíèòü:
431 begin
433 Exit;
438 begin
439 // Lost_Soul íå ìîæåò ðàíèòü Pain_Elemental'à:
441 Exit;
443 // Îáà ìîíñòðà îäíîãî âèäà:
453 begin
456 else
460 end
461 else
466 function HitPlayer (p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean;
467 begin
470 // Ñàì ñåáÿ ìîæåò ðàíèòü òîëüêî ðàêåòîé è òîêîì
474 begin
475 if (t <> HIT_FLAME) or (p.FFireTime = 0) or (vx <> 0) or (vy <> 0) then p.Damage(d, SpawnerUID, vx, vy, t);
486 begin
489 begin
491 begin
496 begin
503 var
508 begin
509 //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255);
521 begin
542 begin
545 else
551 //FIXME
555 function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord;
556 var
558 begin
561 else
562 begin
569 WEAPON_ROCKETLAUNCHER:
570 begin
572 begin
585 WEAPON_PLASMA:
586 begin
588 begin
601 WEAPON_BFG:
602 begin
604 begin
617 WEAPON_FLAMETHROWER:
618 begin
620 begin
634 WEAPON_IMP_FIRE:
635 begin
637 begin
650 WEAPON_CACO_FIRE:
651 begin
653 begin
666 WEAPON_MANCUB_FIRE:
667 begin
669 begin
682 WEAPON_BARON_FIRE:
683 begin
685 begin
698 WEAPON_BSP_FIRE:
699 begin
701 begin
714 WEAPON_SKEL_FIRE:
715 begin
717 begin
741 else
747 var
749 begin
766 else
767 begin
770 else
775 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
776 var
780 var
784 begin
791 begin
794 begin
801 begin
808 break;
813 {
814 function monsCheckHit (monidx: Integer; mon: TMonster): Boolean;
815 begin
816 result := false; // don't stop
817 if mon.Live and g_Obj_Collide(obj, @mon.Obj) then
818 begin
819 if HitMonster(mon, d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then
820 begin
821 if (t <> HIT_FLAME) then
822 begin
823 mon.Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4,
824 (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4);
825 end;
826 result := True;
827 end;
828 end;
829 end;
830 }
833 begin
836 begin
838 begin
847 begin
848 //result := g_Mons_ForEach(monsCheckHit);
849 //FIXME: accelerate this!
850 result := g_Mons_ForEachAliveAt(obj.X+obj.Rect.X, obj.Y+obj.Rect.Y, obj.Rect.Width, obj.Rect.Height, monsCheckHit);
853 begin
857 begin
864 begin
865 // Ðàñïèëèâàåì òðóï:
873 // Êàìïàíèÿ:
875 begin
876 // Ñíà÷àëà áü¸ì ìîíñòðîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü èãðîêîâ
878 begin
880 Exit;
884 begin
886 Exit;
890 // Äåçìàò÷:
891 GM_DM:
892 begin
893 // Ñíà÷àëà áü¸ì èãðîêîâ, åñëè åñòü, ïîòîì ïûòàåìñÿ áèòü ìîíñòðîâ
895 begin
897 Exit;
901 begin
903 Exit;
907 // Êîìàíäíûå:
909 begin
910 // Ñíà÷àëà áü¸ì èãðîêîâ êîìàíäû ñîïåðíèêà
912 begin
914 Exit;
917 // Ïîòîì ìîíñòðîâ
919 begin
921 Exit;
924 // È â êîíöå ñâîèõ èãðîêîâ
926 begin
928 Exit;
936 begin
947 var
951 var
953 begin
955 begin
963 begin
964 //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
965 //e_WriteLog(Format('explo monster #%d: x=%d; y=%d; rad=%d; dx=%d; dy=%d', [monidx, X, Y, rad, dx, dy]), MSG_NOTIFY);
971 begin
972 HitMonster(mon, ((mon.Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET);
980 var
983 begin
996 begin
1004 begin
1005 //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
1006 // PLAYER_RECT.Width, PLAYER_RECT.Height);
1011 HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET);
1016 //g_Mons_ForEach(monsExCheck);
1025 begin
1033 begin
1050 begin
1058 begin
1071 begin
1076 var
1078 begin
1080 begin
1092 begin
1136 g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2);
1144 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True);
1147 g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3);
1158 begin
1215 function GunHitPlayer (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Boolean;
1216 var
1218 begin
1221 begin
1223 begin
1225 begin
1234 function GunHit (X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte;
1237 begin
1240 begin
1246 begin
1253 (*
1254 procedure g_Weapon_gunOld(const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1255 var
1256 a: Integer;
1257 x2, y2: Integer;
1258 dx, dy: Integer;
1259 xe, ye: Integer;
1260 xi, yi: Integer;
1261 s, c: Extended;
1262 //vx, vy: Integer;
1263 xx, yy, d: Integer;
1264 i: Integer;
1265 t1, _collide: Boolean;
1266 w, h: Word;
1267 {$IF DEFINED(D2F_DEBUG)}
1268 stt: UInt64;
1269 showTime: Boolean = true;
1270 {$ENDIF}
1271 begin
1272 a := GetAngle(x, y, xd, yd)+180;
1274 SinCos(DegToRad(-a), s, c);
1276 if Abs(s) < 0.01 then s := 0;
1277 if Abs(c) < 0.01 then c := 0;
1279 x2 := x+Round(c*gMapInfo.Width);
1280 y2 := y+Round(s*gMapInfo.Width);
1282 t1 := gWalls <> nil;
1283 _collide := False;
1284 w := gMapInfo.Width;
1285 h := gMapInfo.Height;
1287 xe := 0;
1288 ye := 0;
1289 dx := x2-x;
1290 dy := y2-y;
1292 if (xd = 0) and (yd = 0) then Exit;
1294 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1295 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1297 dx := Abs(dx);
1298 dy := Abs(dy);
1300 if dx > dy then d := dx else d := dy;
1302 //blood vel, for Monster.Damage()
1303 //vx := (dx*10 div d)*xi;
1304 //vy := (dy*10 div d)*yi;
1306 {$IF DEFINED(D2F_DEBUG)}
1307 stt := curTimeMicro();
1308 {$ENDIF}
1310 xx := x;
1311 yy := y;
1313 for i := 1 to d do
1314 begin
1315 xe := xe+dx;
1316 ye := ye+dy;
1318 if xe > d then
1319 begin
1320 xe := xe-d;
1321 xx := xx+xi;
1322 end;
1324 if ye > d then
1325 begin
1326 ye := ye-d;
1327 yy := yy+yi;
1328 end;
1330 if (yy > h) or (yy < 0) then Break;
1331 if (xx > w) or (xx < 0) then Break;
1333 if t1 then
1334 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1335 begin
1336 _collide := True;
1337 {$IF DEFINED(D2F_DEBUG)}
1338 stt := curTimeMicro()-stt;
1339 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1340 showTime := false;
1341 {$ENDIF}
1342 g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0);
1343 if g_Game_IsServer and g_Game_IsNet then
1344 MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK);
1345 end;
1347 if not _collide then
1348 begin
1349 _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0;
1350 end;
1352 if _collide then Break;
1353 end;
1355 {$IF DEFINED(D2F_DEBUG)}
1356 if showTime then
1357 begin
1358 stt := curTimeMicro()-stt;
1359 e_WriteLog(Format('*** old trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1360 end;
1361 {$ENDIF}
1363 if CheckTrigger and g_Game_IsServer then
1364 g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT);
1365 end;
1366 *)
1369 (*
1370 procedure g_Weapon_gunComplicated (const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1371 const
1372 HHGridSize = 64;
1374 var
1375 hitray: Ray2D;
1376 xi, yi: Integer;
1378 function doPlayerHit (idx: Integer): Boolean;
1379 begin
1380 result := false;
1381 if (idx < 0) or (idx > High(gPlayers)) then exit;
1382 if (gPlayers[idx] = nil) or not gPlayers[idx].Live then exit;
1383 result := HitPlayer(gPlayers[idx], dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1384 if result and (v <> 0) then gPlayers[idx].Push((xi*v), (yi*v));
1385 {$IF DEFINED(D2F_DEBUG)}
1386 //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY);
1387 {$ENDIF}
1388 end;
1390 function doMonsterHit (mon: TMonster): Boolean;
1391 begin
1392 result := false;
1393 if (mon = nil) then exit;
1394 result := HitMonster(mon, dmg, (xi*v)*10, (yi*v)*10-3, SpawnerUID, HIT_SOME);
1395 if result and (v <> 0) then mon.Push((xi*v), (yi*v));
1396 {$IF DEFINED(D2F_DEBUG)}
1397 //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY);
1398 {$ENDIF}
1399 end;
1401 // get nearest player along hitray
1402 // return `true` if instant hit was detected
1403 function playerPossibleHit (): Boolean;
1404 var
1405 i: Integer;
1406 aabb: AABB2D;
1407 tmin: Single;
1408 begin
1409 result := false;
1410 for i := 0 to High(gPlayers) do
1411 begin
1412 if (gPlayers[i] <> nil) and gPlayers[i].Live then
1413 begin
1414 aabb := gPlayers[i].mapAABB;
1415 // inside?
1416 if aabb.contains(x, y) then
1417 begin
1418 if doPlayerHit(i) then begin result := true; exit; end;
1419 end
1420 else if (aabb.intersects(hitray, @tmin)) then
1421 begin
1422 // intersect
1423 if (tmin <= 0) then
1424 begin
1425 if doPlayerHit(i) then begin result := true; exit; end;
1426 end
1427 else
1428 begin
1429 appendHitTimePlr(tmin, i);
1430 end;
1431 end;
1432 end;
1433 end;
1434 end;
1436 function monsPossibleHitInstant (mon: TMonster): Boolean;
1437 var
1438 aabb: AABB2D;
1439 begin
1440 result := false; // don't stop
1441 aabb := mon.mapAABB;
1442 if aabb.contains(x, y) then
1443 begin
1444 result := doMonsterHit(mon);
1445 end;
1446 end;
1448 function monsPossibleHit (mon: TMonster): Boolean;
1449 var
1450 aabb: AABB2D;
1451 tmin: Single;
1452 begin
1453 result := false; // don't stop
1454 if not wgunMonHash.put(Integer(mon.UID), 1) then
1455 begin
1456 // new monster; calculate hitpoint
1457 aabb := mon.mapAABB;
1458 if (aabb.intersects(hitray, @tmin)) then
1459 begin
1460 if (tmin < 0) then tmin := 1.0;
1461 appendHitTimeMon(tmin, mon);
1462 end;
1463 end;
1464 end;
1466 var
1467 a: Integer;
1468 x2, y2: Integer;
1469 dx, dy: Integer;
1470 xe, ye: Integer;
1471 s, c: Extended;
1472 xx, yy, d: Integer;
1473 prevX, prevY: Integer;
1474 leftToNextMonsterQuery: Integer = 0;
1475 i: Integer;
1476 t1: Boolean;
1477 {$IF DEFINED(GWEP_HITSCAN_TRACE_BITMAP_CHECKER)}
1478 w, h: Word;
1479 {$ENDIF}
1480 wallWasHit: Boolean = false;
1481 wallHitX: Integer = 0;
1482 wallHitY: Integer = 0;
1483 didHit: Boolean = false;
1484 mptWX: Integer = 0;
1485 mptWY: Integer = 0;
1486 mptHit: Integer = -1;
1487 {$IF DEFINED(D2F_DEBUG)}
1488 stt: UInt64;
1489 {$ENDIF}
1490 begin
1491 if not gwep_debug_fast_trace then
1492 begin
1493 g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger);
1494 exit;
1495 end;
1497 wgunMonHash.reset(); //FIXME: clear hash on level change
1498 wgunHitHeap.clear();
1499 wgunHitTimeUsed := 0;
1501 a := GetAngle(x, y, xd, yd)+180;
1503 SinCos(DegToRad(-a), s, c);
1505 if Abs(s) < 0.01 then s := 0;
1506 if Abs(c) < 0.01 then c := 0;
1508 x2 := x+Round(c*gMapInfo.Width);
1509 y2 := y+Round(s*gMapInfo.Width);
1511 hitray := Ray2D.Create(x, y, x2, y2);
1513 e_WriteLog(Format('GUN TRACE: (%d,%d) to (%d,%d)', [x, y, x2, y2]), MSG_NOTIFY);
1515 {$IF DEFINED(GWEP_HITSCAN_TRACE_BITMAP_CHECKER)}
1516 t1 := (gWalls <> nil);
1517 w := gMapInfo.Width;
1518 h := gMapInfo.Height;
1519 {$ENDIF}
1521 dx := x2-x;
1522 dy := y2-y;
1524 if (xd = 0) and (yd = 0) then Exit;
1526 if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0;
1527 if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0;
1529 // check instant hits
1530 xx := x;
1531 yy := y;
1532 if (dx < 0) then Dec(xx);
1533 if (dy < 0) then Dec(yy);
1535 dx := Abs(dx);
1536 dy := Abs(dy);
1538 if playerPossibleHit() then exit; // instant hit
1539 if g_Mons_ForEachAliveAt(xx, yy, 3, 3, monsPossibleHitInstant) then exit; // instant hit
1541 if dx > dy then d := dx else d := dy;
1543 //blood vel, for Monster.Damage()
1544 //vx := (dx*10 div d)*xi;
1545 //vy := (dy*10 div d)*yi;
1547 {$IF DEFINED(D2F_DEBUG)}
1548 mptHit := g_Map_traceToNearestWall(x, y, x2, y2, @mptWX, @mptWY);
1549 e_WriteLog(Format('tree trace: (%d,%d)', [mptWX, mptWY]), MSG_NOTIFY);
1550 {$ENDIF}
1552 {$IF not DEFINED(GWEP_HITSCAN_TRACE_BITMAP_CHECKER)}
1553 wallWasHit := (mptHit >= 0);
1554 wallHitX := mptWX;
1555 wallHitY := mptWY;
1556 t1 := false;
1557 {$ENDIF}
1559 {$IF DEFINED(D2F_DEBUG)}
1560 stt := curTimeMicro();
1561 {$ENDIF}
1562 // find wall, collect monsters
1563 begin
1564 xe := 0;
1565 ye := 0;
1566 xx := x;
1567 yy := y;
1568 prevX := xx;
1569 prevY := yy;
1570 for i := 1 to d do
1571 begin
1572 prevX := xx;
1573 prevY := yy;
1574 xe += dx;
1575 ye += dy;
1576 if (xe > d) then begin xe -= d; xx += xi; end;
1577 if (ye > d) then begin ye -= d; yy += yi; end;
1579 // wtf?!
1580 //if (yy > h) or (yy < 0) then break;
1581 //if (xx > w) or (xx < 0) then break;
1583 {$IF DEFINED(GWEP_HITSCAN_TRACE_BITMAP_CHECKER)}
1584 if t1 and (xx >= 0) and (yy >= 0) and (xx < w) and (yy < h) then
1585 begin
1586 if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then
1587 begin
1588 wallWasHit := true;
1589 wallHitX := prevX;
1590 wallHitY := prevY;
1591 end;
1592 end;
1593 {$ELSE}
1594 if (abs(prevX-wallHitX) < 2) and (abs(prevY-wallHitY) < 2) then t1 := true;
1595 {$ENDIF}
1597 if (leftToNextMonsterQuery <> 0) and not wallWasHit then
1598 begin
1599 Dec(leftToNextMonsterQuery);
1600 end
1601 else
1602 begin
1603 // check monsters
1604 g_Mons_ForEachAliveAt(xx-HHGridSize div 2, yy-HHGridSize div 2, HHGridSize+HHGridSize div 2, HHGridSize+HHGridSize div 2, monsPossibleHit);
1605 leftToNextMonsterQuery := HHGridSize; // again
1606 {$IF DEFINED(GWEP_HITSCAN_TRACE_BITMAP_CHECKER)}
1607 if wallWasHit then break;
1608 {$ELSE}
1609 if t1 then break;
1610 {$ENDIF}
1611 end;
1612 end;
1614 if not wallWasHit then
1615 begin
1616 wallHitX := prevX;
1617 wallHitY := prevY;
1618 end;
1619 end;
1621 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1622 // also, if `wallWasHit` is true, then `wallHitX` and `wallHitY` contains wall coords
1623 while (wgunHitHeap.count > 0) do
1624 begin
1625 // has some entities to check, do it
1626 i := wgunHitHeap.front;
1627 wgunHitHeap.popFront();
1628 hitray.atTime(wgunHitTime[i].time, xe, ye);
1629 // check if it is not behind the wall
1630 if ((xe-x)*(xe-x)+(ye-y)*(ye-y) < (wallHitX-x)*(wallHitX-x)+(wallHitY-y)*(wallHitY-y)) then
1631 begin
1632 if (wgunHitTime[i].mon <> nil) then
1633 begin
1634 didHit := doMonsterHit(wgunHitTime[i].mon);
1635 end
1636 else
1637 begin
1638 didHit := doPlayerHit(wgunHitTime[i].plridx);
1639 end;
1640 if didHit then
1641 begin
1642 // need new coords for trigger
1643 wallHitX := xe;
1644 wallHitY := ye;
1645 wallWasHit := false; // no sparks
1646 break;
1647 end;
1648 end;
1649 end;
1651 // need sparks?
1652 if wallWasHit then
1653 begin
1654 {$IF DEFINED(GWEP_HITSCAN_TRACE_BITMAP_CHECKER)}
1655 if (mptHit < 0) then
1656 begin
1657 e_WriteLog('OOPS: tree trace failed, but pixel trace found the wall!', MSG_WARNING);
1658 raise Exception.Create('map tree trace fucked');
1659 end
1660 else
1661 begin
1662 {$IF DEFINED(D2F_DEBUG)}
1663 //e_WriteLog(Format(' trace: (%d,%d)', [wallHitX, wallHitY]), MSG_NOTIFY);
1664 {$ENDIF}
1665 wallHitX := mptWX;
1666 wallHitY := mptWY;
1667 end;
1668 {$ENDIF}
1669 {$IF DEFINED(D2F_DEBUG)}
1670 stt := curTimeMicro()-stt;
1671 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1672 {$ENDIF}
1673 g_GFX_Spark(wallHitX, wallHitY, 2+Random(2), 180+a, 0, 0);
1674 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1675 end
1676 else
1677 begin
1678 {$IF DEFINED(D2F_DEBUG)}
1679 stt := curTimeMicro()-stt;
1680 e_WriteLog(Format('*** new trace time: %u microseconds', [LongWord(stt)]), MSG_NOTIFY);
1681 {$ENDIF}
1682 end;
1684 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1685 end;
1686 *)
1689 procedure g_Weapon_gun (const x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean);
1690 var
1696 begin
1702 {$IF DEFINED(D2F_DEBUG)}
1703 //if result then e_WriteLog(Format(' PLAYER #%d HIT', [idx]), MSG_NOTIFY);
1704 {$ENDIF}
1708 begin
1713 {$IF DEFINED(D2F_DEBUG)}
1714 //if result then e_WriteLog(Format(' MONSTER #%u HIT', [LongWord(mon.UID)]), MSG_NOTIFY);
1715 {$ENDIF}
1718 // collect players along hitray
1719 // return `true` if instant hit was detected
1721 var
1725 begin
1728 begin
1730 begin
1732 // inside?
1734 begin
1736 end
1738 begin
1739 // intersect
1741 begin
1743 end
1744 else
1745 begin
1754 begin
1759 var
1770 {$IF DEFINED(D2F_DEBUG)}
1772 {$ENDIF}
1773 begin
1774 (*
1775 if not gwep_debug_fast_trace then
1776 begin
1777 g_Weapon_gunOld(x, y, xd, yd, v, dmg, SpawnerUID, CheckTrigger);
1778 exit;
1779 end;
1780 *)
1804 {$IF DEFINED(D2F_DEBUG)}
1807 {$ENDIF}
1811 begin
1815 end
1816 else
1817 begin
1826 // collect monsters
1829 // here, we collected all monsters and players in `wgunHitHeap` and `wgunHitTime`
1830 // also, if `wallWasHit` >= 0, then `wallHitX` and `wallHitY` contains spark coords
1832 begin
1833 // has some entities to check, do it
1837 // check if it is not behind the wall
1839 begin
1841 end
1842 else
1843 begin
1847 begin
1848 // need new coords for trigger
1852 break;
1856 // need sparks?
1858 begin
1859 {$IF DEFINED(D2F_DEBUG)}
1862 {$ENDIF}
1864 if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(wallHitX, wallHitY, 180+a, NET_GFX_SPARK);
1865 end
1866 else
1867 begin
1868 {$IF DEFINED(D2F_DEBUG)}
1871 {$ENDIF}
1874 if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, wallHitX, wallHitY, SpawnerUID, ACTIVATE_SHOT);
1879 var
1881 begin
1895 else
1900 var
1902 begin
1919 var
1922 begin
1925 else
1926 begin
1933 begin
1958 var
1961 begin
1964 else
1965 begin
1972 begin
1998 var
2001 begin
2004 else
2005 begin
2012 begin
2037 var
2040 begin
2043 else
2044 begin
2051 begin
2071 // if not Silent then
2072 // g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y);
2077 var
2080 begin
2083 else
2084 begin
2091 begin
2116 var
2119 begin
2122 else
2123 begin
2130 begin
2155 var
2158 begin
2161 else
2162 begin
2169 begin
2194 var
2197 begin
2200 else
2201 begin
2208 begin
2234 var
2237 begin
2240 else
2241 begin
2248 begin
2274 var
2277 begin
2280 else
2281 begin
2288 begin
2312 var
2315 begin
2317 begin
2326 begin
2332 begin
2340 begin
2347 begin
2355 var
2357 begin
2362 begin
2370 var
2372 begin
2378 begin
2385 var
2396 begin
2398 Exit;
2401 begin
2403 Continue;
2408 begin
2412 // Àêòèâèðîâàòü òðèããåðû ïî ïóòè (êðîìå óæå àêòèâèðîâàííûõ):
2416 else
2420 begin
2421 // Ïîïîëíÿåì ñïèñîê àêòèâèðîâàííûõ òðèããåðîâ:
2423 triggers := t
2424 else
2425 begin
2430 begin
2437 // Àíèìàöèÿ ñíàðÿäà:
2441 // Äâèæåíèå:
2448 begin
2450 end
2451 else
2452 begin
2459 begin
2460 // Íà êëèåíòå ñêîðåå âñåãî è òàê óæå âûïàë.
2463 Continue;
2471 begin
2472 // Âûëåòåëà èç âîäû:
2476 // Â âîäå øëåéô - ïóçûðè, â âîçäóõå øëåéô - äûì:
2481 else
2483 begin
2492 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2496 begin
2505 begin
2512 end
2513 else
2516 begin
2541 begin
2542 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2544 begin
2548 Continue;
2551 // Âåëè÷èíà óðîíà:
2555 else
2561 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2565 begin
2568 else
2571 // Âçðûâ Ïëàçìû:
2573 begin
2588 begin
2589 // Ñî âðåìåíåì óìèðàåò
2591 begin
2593 Continue;
2595 // Ïîä âîäîé òîæå
2597 begin
2599 begin
2601 begin
2611 end
2612 else
2615 Continue;
2618 // Ãðàâèòàöèÿ
2621 // Ïîïàëè â ñòåíó èëè â âîäó:
2623 begin
2624 // Ïðèëèïàåì:
2629 Stopped := MOVE_HITWALL
2631 Stopped := MOVE_HITLAND
2637 // Åñëè â êîãî-òî ïîïàëè
2639 begin
2640 // HIT_FLAME ñàì ïîäîææåò
2641 // Åñëè â ïîëåòå ïîïàëè, èñ÷åçàåì
2648 else
2652 begin
2663 //g_DynLightExplosion(tcx, tcy, 1, 1, 0.8, 0.3);
2668 begin
2669 // Ïîïàëà â âîäó - ýëåêòðîøîê ïî âîäå:
2671 begin
2675 Continue;
2678 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2682 begin
2683 // Ëó÷è BFG:
2686 // Âçðûâ BFG:
2688 begin
2702 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
2703 begin
2704 // Âûëåòåë èç âîäû:
2708 // Âåëè÷èíà óðîíà:
2711 else
2714 else
2717 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2721 begin
2724 else
2727 else
2730 // Âçðûâ:
2732 begin
2746 begin
2747 // Âûëåòåë èç âîäû:
2751 // Ïîïàëè â êîãî-òî èëè â ñòåíó:
2755 begin
2756 // Âçðûâ:
2758 begin
2772 // Åñëè ñíàðÿäà óæå íåò, óäàëÿåì àíèìàöèþ:
2774 begin
2778 begin
2782 end
2783 else if (ShotType <> WEAPON_FLAMETHROWER) and ((oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y)) then
2791 var
2795 begin
2797 Exit;
2802 begin
2808 else
2815 begin
2820 else
2822 end
2824 begin
2832 begin
2843 var
2845 begin
2849 Exit;
2859 begin
2861 Exit;
2866 var
2869 begin
2870 // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ:
2879 // Êîëè÷åñòâî ñíàðÿäîâ:
2883 Exit;
2887 begin
2888 // Ñèãíàòóðà ñíàðÿäà:
2891 // Òèï ñíàðÿäà:
2893 // Öåëü:
2895 // UID ñòðåëÿâøåãî:
2897 // Ðàçìåð ïîëÿ Triggers:
2900 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2903 // Îáúåêò ñíàðÿäà:
2905 // Êîñòûëèíà åáàíàÿ:
2911 var
2914 begin
2916 Exit;
2918 // Êîëè÷åñòâî ñíàðÿäîâ:
2924 Exit;
2927 begin
2928 // Ñèãíàòóðà ñíàðÿäà:
2931 begin
2934 // Òèï ñíàðÿäà:
2936 // Öåëü:
2938 // UID ñòðåëÿâøåãî:
2940 // Ðàçìåð ïîëÿ Triggers:
2943 // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
2946 // Îáúåêò ïðåäìåòà:
2948 // Êîñòûëèíà åáàíàÿ:
2951 // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè:
2957 begin
2960 WEAPON_PLASMA:
2961 begin
2965 WEAPON_BFG:
2966 begin
2970 WEAPON_IMP_FIRE:
2971 begin
2975 WEAPON_BSP_FIRE:
2976 begin
2980 WEAPON_CACO_FIRE:
2981 begin
2985 WEAPON_BARON_FIRE:
2986 begin
2990 WEAPON_MANCUB_FIRE:
2991 begin
3000 var
3004 begin
3006 Exit;
3010 begin
3019 begin
3021 begin
3025 begin
3031 end
3032 else
3035 begin
3047 begin
3050 else
3054 begin
3065 begin
3066 // Âçðûâ BFG:
3068 begin
3078 WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Âûñòðåëû Áåñà, Êàêîäåìîíà Ðûöàðÿ/Áàðîíà àäà
3079 begin
3082 else
3085 else
3089 begin
3100 begin
3102 begin
3120 var
3122 begin
3125 begin
3139 begin
3141 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 128, 0, 0.3, 1, 0.4)
3143 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 128, 0, 1, 0, 0.5)
3145 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 42, 1, 0.8, 0, 0.4)
3146 else
3147 g_AddDynLight(Shots[i].Obj.X+(Shots[i].Obj.Rect.Width div 2), Shots[i].Obj.Y+(Shots[i].Obj.Rect.Height div 2), 128, 1, 0, 0, 0.4);