unit g_weapons; interface uses g_textures, g_basic, e_graphics, g_phys, BinEditor; const HIT_SOME = 0; HIT_ROCKET = 1; HIT_BFG = 2; HIT_TRAP = 3; HIT_FALL = 4; HIT_WATER = 5; HIT_ACID = 6; HIT_ELECTRO = 7; HIT_FLAME = 8; HIT_SELF = 9; HIT_DISCON = 10; type TShot = record ShotType: Byte; Target: Word; SpawnerUID: Word; Triggers: DWArray; Obj: TObj; Animation: TAnimation; TextureID: DWORD; Timeout: DWORD; end; var Shots: array of TShot = nil; LastShotID: Integer = 0; procedure g_Weapon_LoadData(); procedure g_Weapon_FreeData(); procedure g_Weapon_Init(); procedure g_Weapon_Free(); function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte; function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean; function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord; procedure g_Weapon_gun(x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean); procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word); function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer; procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: Integer = -1; Silent: Boolean = False); procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); procedure g_Weapon_bfghit(x, y: Integer); procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False); procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False); procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False); procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False); function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean; procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word); procedure g_Weapon_Update(); procedure g_Weapon_Draw(); function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean; procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True); procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter); procedure g_Weapon_LoadState(var Mem: TBinMemoryReader); const WEAPON_KASTET = 0; WEAPON_SAW = 1; WEAPON_PISTOL = 2; WEAPON_SHOTGUN1 = 3; WEAPON_SHOTGUN2 = 4; WEAPON_CHAINGUN = 5; WEAPON_ROCKETLAUNCHER = 6; WEAPON_PLASMA = 7; WEAPON_BFG = 8; WEAPON_SUPERPULEMET = 9; WEAPON_MEGAKASTET = 10; WEAPON_ZOMBY_PISTOL = 20; WEAPON_IMP_FIRE = 21; WEAPON_BSP_FIRE = 22; WEAPON_CACO_FIRE = 23; WEAPON_BARON_FIRE = 24; WEAPON_MANCUB_FIRE = 25; WEAPON_SKEL_FIRE = 26; implementation uses Math, g_map, g_player, g_gfx, g_sound, g_main, g_console, SysUtils, g_options, g_game, g_triggers, MAPDEF, e_log, g_monsters, g_saveload, g_language, g_netmsg; type TWaterPanel = record X, Y: Integer; Width, Height: Word; Active: Boolean; end; const SHOT_ROCKETLAUNCHER_WIDTH = 27; SHOT_ROCKETLAUNCHER_HEIGHT = 12; SHOT_SKELFIRE_WIDTH = 32; SHOT_SKELFIRE_HEIGHT = 16; SHOT_PLASMA_WIDTH = 16; SHOT_PLASMA_HEIGHT = 16; SHOT_BFG_WIDTH = 32; SHOT_BFG_HEIGHT = 32; SHOT_BFG_DAMAGE = 100; SHOT_BFG_RADIUS = 256; SHOT_SIGNATURE = $544F4853; // 'SHOT' var WaterMap: array of array of DWORD = nil; function FindShot(): DWORD; var i: Integer; begin if Shots <> nil then for i := 0 to High(Shots) do if Shots[i].ShotType = 0 then begin Result := i; LastShotID := Result; Exit; end; if Shots = nil then begin SetLength(Shots, 128); Result := 0; end else begin Result := High(Shots) + 1; SetLength(Shots, Length(Shots) + 128); end; LastShotID := Result; end; procedure CreateWaterMap(); var WaterArray: Array of TWaterPanel; a, b, c, m: Integer; ok: Boolean; begin if gWater = nil then Exit; SetLength(WaterArray, Length(gWater)); for a := 0 to High(gWater) do begin WaterArray[a].X := gWater[a].X; WaterArray[a].Y := gWater[a].Y; WaterArray[a].Width := gWater[a].Width; WaterArray[a].Height := gWater[a].Height; WaterArray[a].Active := True; end; g_Game_SetLoadingText(_lc[I_LOAD_WATER_MAP], High(WaterArray), False); for a := 0 to High(WaterArray) do if WaterArray[a].Active then begin WaterArray[a].Active := False; m := Length(WaterMap); SetLength(WaterMap, m+1); SetLength(WaterMap[m], 1); WaterMap[m][0] := a; ok := True; while ok do begin ok := False; for b := 0 to High(WaterArray) do if WaterArray[b].Active then for c := 0 to High(WaterMap[m]) do if g_CollideAround(WaterArray[b].X, WaterArray[b].Y, WaterArray[b].Width, WaterArray[b].Height, WaterArray[WaterMap[m][c]].X, WaterArray[WaterMap[m][c]].Y, WaterArray[WaterMap[m][c]].Width, WaterArray[WaterMap[m][c]].Height) then begin WaterArray[b].Active := False; SetLength(WaterMap[m], Length(WaterMap[m])+1); WaterMap[m][High(WaterMap[m])] := b; ok := True; Break; end; end; g_Game_StepLoading(); end; WaterArray := nil; end; procedure CheckTrap(ID: DWORD; dm: Integer; t: Byte); var a, b, c, d, i1, i2: Integer; pl, mn: WArray; begin if (gWater = nil) or (WaterMap = nil) then Exit; i1 := -1; i2 := -1; SetLength(pl, 1024); SetLength(mn, 1024); for d := 0 to 1023 do pl[d] := $FFFF; for d := 0 to 1023 do mn[d] := $FFFF; for a := 0 to High(WaterMap) do for b := 0 to High(WaterMap[a]) do begin if not g_Obj_Collide(gWater[WaterMap[a][b]].X, gWater[WaterMap[a][b]].Y, gWater[WaterMap[a][b]].Width, gWater[WaterMap[a][b]].Height, @Shots[ID].Obj) then Continue; for c := 0 to High(WaterMap[a]) do begin if gPlayers <> nil then begin for d := 0 to High(gPlayers) do if (gPlayers[d] <> nil) and (gPlayers[d].Live) then if gPlayers[d].Collide(gWater[WaterMap[a][c]]) then if not InWArray(d, pl) then if i1 < 1023 then begin i1 := i1+1; pl[i1] := d; end; end; if gMonsters <> nil then begin for d := 0 to High(gMonsters) do if (gMonsters[d] <> nil) and (gMonsters[d].Live) then if gMonsters[d].Collide(gWater[WaterMap[a][c]]) then if not InWArray(d, mn) then if i2 < 1023 then begin i2 := i2+1; mn[i2] := d; end; end; end; if i1 <> -1 then for d := 0 to i1 do gPlayers[pl[d]].Damage(dm, Shots[ID].SpawnerUID, 0, 0, t); if i2 <> -1 then for d := 0 to i2 do gMonsters[mn[d]].Damage(dm, 0, 0, Shots[ID].SpawnerUID, t); end; pl := nil; mn := nil; end; function HitMonster(m: TMonster; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean; var tt, mt: Byte; mon: TMonster; begin Result := False; tt := g_GetUIDType(SpawnerUID); if tt = UID_MONSTER then begin mon := g_Monsters_Get(SpawnerUID); if mon <> nil then mt := g_Monsters_Get(SpawnerUID).MonsterType else mt := 0; end else mt := 0; if m = nil then Exit; if m.UID = SpawnerUID then begin // Сам себя может ранить только ракетой и током: if (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then Exit; // Кибер демон и бочка вообще не могут себя ранить: if (m.MonsterType = MONSTER_CYBER) or (m.MonsterType = MONSTER_BARREL) then begin Result := True; Exit; end; end; if tt = UID_MONSTER then begin // Lost_Soul не может ранить Pain_Elemental'а: if (mt = MONSTER_SOUL) and (m.MonsterType = MONSTER_PAIN) then Exit; // Оба монстра одного вида: if mt = m.MonsterType then case mt of MONSTER_IMP, MONSTER_DEMON, MONSTER_BARON, MONSTER_KNIGHT, MONSTER_CACO, MONSTER_SOUL, MONSTER_MANCUB, MONSTER_SKEL, MONSTER_FISH: Exit; // Эти не бьют своих end; end; if g_Game_IsServer then Result := m.Damage(d, vx, vy, SpawnerUID, t) else Result := True; end; function HitPlayer(p: TPlayer; d: Integer; vx, vy: Integer; SpawnerUID: Word; t: Byte): Boolean; begin Result := False; // Сам себя может ранить только ракетой и током: if (p.UID = SpawnerUID) and (t <> HIT_ROCKET) and (t <> HIT_ELECTRO) then Exit; if g_Game_IsServer then p.Damage(d, SpawnerUID, vx, vy, t); Result := True; end; function GunHit(X, Y: Integer; vx, vy: Integer; dmg: Integer; SpawnerUID: Word; AllowPush: Boolean): Byte; var i, h: Integer; begin Result := 0; h := High(gPlayers); if h <> -1 then for i := 0 to h do if (gPlayers[i] <> nil) and gPlayers[i].Live and gPlayers[i].Collide(X, Y) then if HitPlayer(gPlayers[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then begin if AllowPush then gPlayers[i].Push(vx, vy); Result := 1; end; if Result <> 0 then Exit; h := High(gMonsters); if h <> -1 then for i := 0 to h do if (gMonsters[i] <> nil) and gMonsters[i].Live and gMonsters[i].Collide(X, Y) then if HitMonster(gMonsters[i], dmg, vx*10, vy*10-3, SpawnerUID, HIT_SOME) then begin if AllowPush then gMonsters[i].Push(vx, vy); Result := 2; Exit; end; end; procedure g_Weapon_BFG9000(X, Y: Integer; SpawnerUID: Word); var i, h: Integer; st: Byte; pl: TPlayer; b: Boolean; begin //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255); h := High(gCorpses); if gAdvCorpses and (h <> -1) then for i := 0 to h do if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then with gCorpses[i] do if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2), Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2), Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then begin Damage(50, 0, 0); g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2), Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)); end; st := TEAM_NONE; pl := g_Player_Get(SpawnerUID); if pl <> nil then st := pl.Team; h := High(gPlayers); if h <> -1 then for i := 0 to h do if (gPlayers[i] <> nil) and (gPlayers[i].Live) and (gPlayers[i].UID <> SpawnerUID) then with gPlayers[i] do if (g_PatchLength(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2), GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) <= SHOT_BFG_RADIUS) and g_TraceVector(X, Y, GameX+PLAYER_RECT.X+(PLAYER_RECT.Width div 2), GameY+PLAYER_RECT.Y+(PLAYER_RECT.Height div 2)) then begin if (st = TEAM_NONE) or (st <> gPlayers[i].Team) then b := HitPlayer(gPlayers[i], 50, 0, 0, SpawnerUID, HIT_SOME) else b := HitPlayer(gPlayers[i], 25, 0, 0, SpawnerUID, HIT_SOME); if b then gPlayers[i].BFGHit(); end; h := High(gMonsters); if h <> -1 then for i := 0 to h do if (gMonsters[i] <> nil) and (gMonsters[i].Live) and (gMonsters[i].UID <> SpawnerUID) then with gMonsters[i] do if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2), Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2), Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then if HitMonster(gMonsters[i], 50, 0, 0, SpawnerUID, HIT_SOME) then gMonsters[i].BFGHit(); end; function g_Weapon_CreateShot(I: Integer; ShotType: Byte; Spawner, TargetUID: Word; X, Y, XV, YV: Integer): LongWord; var find_id, FramesID: DWORD; begin if I < 0 then find_id := FindShot() else begin find_id := I; if Integer(find_id) >= High(Shots) then SetLength(Shots, find_id + 64) end; case ShotType of WEAPON_ROCKETLAUNCHER: begin with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH; Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT; Animation := nil; Triggers := nil; ShotType := WEAPON_ROCKETLAUNCHER; g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID); end; end; WEAPON_PLASMA: begin with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := SHOT_PLASMA_WIDTH; Obj.Rect.Height := SHOT_PLASMA_HEIGHT; Triggers := nil; ShotType := WEAPON_PLASMA; g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA'); Animation := TAnimation.Create(FramesID, True, 5); end; end; WEAPON_BFG: begin with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := SHOT_BFG_WIDTH; Obj.Rect.Height := SHOT_BFG_HEIGHT; Triggers := nil; ShotType := WEAPON_BFG; g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG'); Animation := TAnimation.Create(FramesID, True, 6); end; end; WEAPON_IMP_FIRE: begin with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := 16; Obj.Rect.Height := 16; Triggers := nil; ShotType := WEAPON_IMP_FIRE; g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE'); Animation := TAnimation.Create(FramesID, True, 4); end; end; WEAPON_CACO_FIRE: begin with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := 16; Obj.Rect.Height := 16; Triggers := nil; ShotType := WEAPON_CACO_FIRE; g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE'); Animation := TAnimation.Create(FramesID, True, 4); end; end; WEAPON_MANCUB_FIRE: begin with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := 32; Obj.Rect.Height := 32; Triggers := nil; ShotType := WEAPON_MANCUB_FIRE; g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE'); Animation := TAnimation.Create(FramesID, True, 4); end; end; WEAPON_BARON_FIRE: begin with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := 32; Obj.Rect.Height := 16; Triggers := nil; ShotType := WEAPON_BARON_FIRE; g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE'); Animation := TAnimation.Create(FramesID, True, 4); end; end; WEAPON_BSP_FIRE: begin with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := 16; Obj.Rect.Height := 16; Triggers := nil; ShotType := WEAPON_BSP_FIRE; g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE'); Animation := TAnimation.Create(FramesID, True, 4); end; end; WEAPON_SKEL_FIRE: begin with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := SHOT_SKELFIRE_WIDTH; Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT; Triggers := nil; ShotType := WEAPON_SKEL_FIRE; target := TargetUID; g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE'); Animation := TAnimation.Create(FramesID, True, 5); end; end; end; Shots[find_id].Obj.X := X; Shots[find_id].Obj.Y := Y; Shots[find_id].Obj.Vel.X := XV; Shots[find_id].Obj.Vel.Y := YV; Shots[find_id].Obj.Accel.X := 0; Shots[find_id].Obj.Accel.Y := 0; Shots[find_id].SpawnerUID := Spawner; Result := find_id; end; procedure throw(i, x, y, xd, yd, s: Integer); var a: Integer; begin yd := yd - y; xd := xd - x; a := Max(Abs(xd), Abs(yd)); if a = 0 then a := 1; Shots[i].Obj.X := x; Shots[i].Obj.Y := y; Shots[i].Obj.Vel.X := (xd*s) div a; Shots[i].Obj.Vel.Y := (yd*s) div a; Shots[i].Obj.Accel.X := 0; Shots[i].Obj.Accel.Y := 0; if Shots[i].ShotType in [WEAPON_ROCKETLAUNCHER, WEAPON_BFG] then Shots[i].Timeout := 900 // ~25 sec else Shots[i].Timeout := 550 // ~15 sec end; function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte; var i, h: Integer; function PlayerHit(Team: Byte = 0): Boolean; var i: Integer; ChkTeam: Boolean; p: TPlayer; begin Result := False; h := High(gPlayers); if h <> -1 then for i := 0 to h do if (gPlayers[i] <> nil) and gPlayers[i].Live and g_Obj_Collide(obj, @gPlayers[i].Obj) then begin ChkTeam := True; if (Team > 0) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then begin p := g_Player_Get(SpawnerUID); if p <> nil then ChkTeam := (p.Team = gPlayers[i].Team) xor (Team = 2); end; if ChkTeam then if HitPlayer(gPlayers[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then begin gPlayers[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4, (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4); if t = HIT_BFG then g_Game_DelayEvent(DE_BFGHIT, 1000, SpawnerUID); Result := True; break; end; end; end; function MonsterHit(): Boolean; var i: Integer; begin Result := False; h := High(gMonsters); if h <> -1 then for i := 0 to h do if (gMonsters[i] <> nil) and gMonsters[i].Live and g_Obj_Collide(obj, @gMonsters[i].Obj) then if HitMonster(gMonsters[i], d, obj^.Vel.X, obj^.Vel.Y, SpawnerUID, t) then begin gMonsters[i].Push((obj^.Vel.X+obj^.Accel.X)*IfThen(t = HIT_BFG, 8, 1) div 4, (obj^.Vel.Y+obj^.Accel.Y)*IfThen(t = HIT_BFG, 8, 1) div 4); Result := True; break; end; end; begin Result := 0; if HitCorpses then begin h := High(gCorpses); if gAdvCorpses and (h <> -1) then for i := 0 to h do if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and g_Obj_Collide(obj, @gCorpses[i].Obj) then begin // Распиливаем труп: gCorpses[i].Damage(d, (obj^.Vel.X+obj^.Accel.X) div 4, (obj^.Vel.Y+obj^.Accel.Y) div 4); Result := 1; end; end; case gGameSettings.GameMode of // Кампания: GM_COOP, GM_SINGLE: begin // Сначала бьём монстров, если есть, потом пытаемся бить игроков if MonsterHit() then begin Result := 2; Exit; end; if PlayerHit() then begin Result := 1; Exit; end; end; // Дезматч: GM_DM: begin // Сначала бьём игроков, если есть, потом пытаемся бить монстров if PlayerHit() then begin Result := 1; Exit; end; if MonsterHit() then begin Result := 2; Exit; end; end; // Командные: GM_TDM, GM_CTF: begin // Сначала бьём игроков команды соперника if PlayerHit(2) then begin Result := 1; Exit; end; // Потом монстров if MonsterHit() then begin Result := 2; Exit; end; // И в конце своих игроков if PlayerHit(1) then begin Result := 1; Exit; end; end; end; end; function g_Weapon_HitUID(UID: Word; d: Integer; SpawnerUID: Word; t: Byte): Boolean; begin Result := False; case g_GetUIDType(UID) of UID_PLAYER: Result := HitPlayer(g_Player_Get(UID), d, 0, 0, SpawnerUID, t); UID_MONSTER: Result := HitMonster(g_Monsters_Get(UID), d, 0, 0, SpawnerUID, t); else Exit; end; end; function g_Weapon_Explode(X, Y: Integer; rad: Integer; SpawnerUID: Word): Boolean; var i, h, r, dx, dy, m, mm: Integer; _angle: SmallInt; begin Result := False; g_Triggers_PressC(X, Y, rad, SpawnerUID, ACTIVATE_SHOT); r := rad*rad; h := High(gPlayers); if h <> -1 then for i := 0 to h do if (gPlayers[i] <> nil) and gPlayers[i].Live then with gPlayers[i] do begin dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X; dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y; if dx > 1000 then dx := 1000; if dy > 1000 then dy := 1000; if dx*dx+dy*dy < r then begin //m := PointToRect(X, Y, GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y, // PLAYER_RECT.Width, PLAYER_RECT.Height); mm := Max(abs(dx), abs(dy)); if mm = 0 then mm := 1; HitPlayer(gPlayers[i], (100*(rad-mm)) div rad, (dx*10) div mm, (dy*10) div mm, SpawnerUID, HIT_ROCKET); gPlayers[i].Push((dx*7) div mm, (dy*7) div mm); end; end; h := High(gMonsters); if h <> -1 then for i := 0 to h do if gMonsters[i] <> nil then with gMonsters[i] do begin dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X; dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y; if dx > 1000 then dx := 1000; if dy > 1000 then dy := 1000; if dx*dx+dy*dy < r then begin //m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, // Obj.Rect.Width, Obj.Rect.Height); mm := Max(abs(dx), abs(dy)); if mm = 0 then mm := 1; if gMonsters[i].Live then HitMonster(gMonsters[i], ((gMonsters[i].Obj.Rect.Width div 4)*10*(rad-mm)) div rad, 0, 0, SpawnerUID, HIT_ROCKET); gMonsters[i].Push((dx*7) div mm, (dy*7) div mm); end; end; h := High(gCorpses); if gAdvCorpses and (h <> -1) then for i := 0 to h do if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then with gCorpses[i] do begin dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X; dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y; if dx > 1000 then dx := 1000; if dy > 1000 then dy := 1000; if dx*dx+dy*dy < r then begin m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height); mm := Max(abs(dx), abs(dy)); if mm = 0 then mm := 1; Damage(Round(100*(rad-m)/rad), (dx*10) div mm, (dy*10) div mm); end; end; h := High(gGibs); if gAdvGibs and (h <> -1) then for i := 0 to h do if gGibs[i].Live then with gGibs[i] do begin dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X; dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y; if dx > 1000 then dx := 1000; if dy > 1000 then dy := 1000; if dx*dx+dy*dy < r then begin m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height); _angle := GetAngle(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2), Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2), X, Y); g_Obj_PushA(@Obj, Round(15*(rad-m)/rad), _angle); end; end; end; procedure g_Weapon_Init(); begin CreateWaterMap(); end; procedure g_Weapon_Free(); var i: Integer; begin if Shots <> nil then begin for i := 0 to High(Shots) do if Shots[i].ShotType <> 0 then Shots[i].Animation.Free(); Shots := nil; end; WaterMap := nil; end; procedure g_Weapon_LoadData(); begin e_WriteLog('Loading weapons data...', MSG_NOTIFY); g_Sound_CreateWADEx('SOUND_WEAPON_HITPUNCH', GameWAD+':SOUNDS\HITPUNCH'); g_Sound_CreateWADEx('SOUND_WEAPON_MISSPUNCH', GameWAD+':SOUNDS\MISSPUNCH'); g_Sound_CreateWADEx('SOUND_WEAPON_HITBERSERK', GameWAD+':SOUNDS\HITBERSERK'); g_Sound_CreateWADEx('SOUND_WEAPON_MISSBERSERK', GameWAD+':SOUNDS\MISSBERSERK'); g_Sound_CreateWADEx('SOUND_WEAPON_SELECTSAW', GameWAD+':SOUNDS\SELECTSAW'); g_Sound_CreateWADEx('SOUND_WEAPON_IDLESAW', GameWAD+':SOUNDS\IDLESAW'); g_Sound_CreateWADEx('SOUND_WEAPON_HITSAW', GameWAD+':SOUNDS\HITSAW'); g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN2', GameWAD+':SOUNDS\FIRESHOTGUN2'); g_Sound_CreateWADEx('SOUND_WEAPON_FIRESHOTGUN', GameWAD+':SOUNDS\FIRESHOTGUN'); g_Sound_CreateWADEx('SOUND_WEAPON_FIRESAW', GameWAD+':SOUNDS\FIRESAW'); g_Sound_CreateWADEx('SOUND_WEAPON_FIREROCKET', GameWAD+':SOUNDS\FIREROCKET'); g_Sound_CreateWADEx('SOUND_WEAPON_FIREPLASMA', GameWAD+':SOUNDS\FIREPLASMA'); g_Sound_CreateWADEx('SOUND_WEAPON_FIREPISTOL', GameWAD+':SOUNDS\FIREPISTOL'); g_Sound_CreateWADEx('SOUND_WEAPON_FIRECGUN', GameWAD+':SOUNDS\FIRECGUN'); g_Sound_CreateWADEx('SOUND_WEAPON_FIREBFG', GameWAD+':SOUNDS\FIREBFG'); g_Sound_CreateWADEx('SOUND_FIRE', GameWAD+':SOUNDS\FIRE'); g_Sound_CreateWADEx('SOUND_WEAPON_STARTFIREBFG', GameWAD+':SOUNDS\STARTFIREBFG'); g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEROCKET', GameWAD+':SOUNDS\EXPLODEROCKET'); g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBFG', GameWAD+':SOUNDS\EXPLODEBFG'); g_Sound_CreateWADEx('SOUND_WEAPON_BFGWATER', GameWAD+':SOUNDS\BFGWATER'); g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEPLASMA', GameWAD+':SOUNDS\EXPLODEPLASMA'); g_Sound_CreateWADEx('SOUND_WEAPON_PLASMAWATER', GameWAD+':SOUNDS\PLASMAWATER'); g_Sound_CreateWADEx('SOUND_WEAPON_FIREBALL', GameWAD+':SOUNDS\FIREBALL'); g_Sound_CreateWADEx('SOUND_WEAPON_EXPLODEBALL', GameWAD+':SOUNDS\EXPLODEBALL'); g_Sound_CreateWADEx('SOUND_WEAPON_FIREREV', GameWAD+':SOUNDS\FIREREV'); g_Sound_CreateWADEx('SOUND_PLAYER_JETFLY', GameWAD+':SOUNDS\WORKJETPACK'); g_Sound_CreateWADEx('SOUND_PLAYER_JETON', GameWAD+':SOUNDS\STARTJETPACK'); g_Sound_CreateWADEx('SOUND_PLAYER_JETOFF', GameWAD+':SOUNDS\STOPJETPACK'); g_Sound_CreateWADEx('SOUND_PLAYER_CASING1', GameWAD+':SOUNDS\CASING1'); g_Sound_CreateWADEx('SOUND_PLAYER_CASING2', GameWAD+':SOUNDS\CASING2'); g_Sound_CreateWADEx('SOUND_PLAYER_SHELL1', GameWAD+':SOUNDS\SHELL1'); g_Sound_CreateWADEx('SOUND_PLAYER_SHELL2', GameWAD+':SOUNDS\SHELL2'); g_Texture_CreateWADEx('TEXTURE_WEAPON_ROCKET', GameWAD+':TEXTURES\BROCKET'); g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_SKELFIRE', GameWAD+':TEXTURES\BSKELFIRE', 64, 16, 2); g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BFG', GameWAD+':TEXTURES\BBFG', 64, 64, 2); g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_PLASMA', GameWAD+':TEXTURES\BPLASMA', 16, 16, 2); g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_IMPFIRE', GameWAD+':TEXTURES\BIMPFIRE', 16, 16, 2); g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BSPFIRE', GameWAD+':TEXTURES\BBSPFIRE', 16, 16, 2); g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_CACOFIRE', GameWAD+':TEXTURES\BCACOFIRE', 16, 16, 2); g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_BARONFIRE', GameWAD+':TEXTURES\BBARONFIRE', 64, 16, 2); g_Frames_CreateWAD(nil, 'FRAMES_WEAPON_MANCUBFIRE', GameWAD+':TEXTURES\BMANCUBFIRE', 64, 32, 2); g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_ROCKET', GameWAD+':TEXTURES\EROCKET', 128, 128, 6); g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_SKELFIRE', GameWAD+':TEXTURES\ESKELFIRE', 64, 64, 3); g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BFG', GameWAD+':TEXTURES\EBFG', 128, 128, 6); g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_IMPFIRE', GameWAD+':TEXTURES\EIMPFIRE', 64, 64, 3); g_Frames_CreateWAD(nil, 'FRAMES_BFGHIT', GameWAD+':TEXTURES\BFGHIT', 64, 64, 4); g_Frames_CreateWAD(nil, 'FRAMES_FIRE', GameWAD+':TEXTURES\FIRE', 64, 128, 8); g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_PLASMA', GameWAD+':TEXTURES\EPLASMA', 32, 32, 4, True); g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BSPFIRE', GameWAD+':TEXTURES\EBSPFIRE', 32, 32, 5); g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_CACOFIRE', GameWAD+':TEXTURES\ECACOFIRE', 64, 64, 3); g_Frames_CreateWAD(nil, 'FRAMES_EXPLODE_BARONFIRE', GameWAD+':TEXTURES\EBARONFIRE', 64, 64, 3); g_Frames_CreateWAD(nil, 'FRAMES_SMOKE', GameWAD+':TEXTURES\SMOKE', 32, 32, 10, False); g_Texture_CreateWADEx('TEXTURE_SHELL_BULLET', GameWAD+':TEXTURES\EBULLET'); g_Texture_CreateWADEx('TEXTURE_SHELL_SHELL', GameWAD+':TEXTURES\ESHELL'); end; procedure g_Weapon_FreeData(); begin e_WriteLog('Releasing weapons data...', MSG_NOTIFY); g_Sound_Delete('SOUND_WEAPON_HITPUNCH'); g_Sound_Delete('SOUND_WEAPON_MISSPUNCH'); g_Sound_Delete('SOUND_WEAPON_HITBERSERK'); g_Sound_Delete('SOUND_WEAPON_MISSBERSERK'); g_Sound_Delete('SOUND_WEAPON_SELECTSAW'); g_Sound_Delete('SOUND_WEAPON_IDLESAW'); g_Sound_Delete('SOUND_WEAPON_HITSAW'); g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN2'); g_Sound_Delete('SOUND_WEAPON_FIRESHOTGUN'); g_Sound_Delete('SOUND_WEAPON_FIRESAW'); g_Sound_Delete('SOUND_WEAPON_FIREROCKET'); g_Sound_Delete('SOUND_WEAPON_FIREPLASMA'); g_Sound_Delete('SOUND_WEAPON_FIREPISTOL'); g_Sound_Delete('SOUND_WEAPON_FIRECGUN'); g_Sound_Delete('SOUND_WEAPON_FIREBFG'); g_Sound_Delete('SOUND_FIRE'); g_Sound_Delete('SOUND_WEAPON_STARTFIREBFG'); g_Sound_Delete('SOUND_WEAPON_EXPLODEROCKET'); g_Sound_Delete('SOUND_WEAPON_EXPLODEBFG'); g_Sound_Delete('SOUND_WEAPON_BFGWATER'); g_Sound_Delete('SOUND_WEAPON_EXPLODEPLASMA'); g_Sound_Delete('SOUND_WEAPON_PLASMAWATER'); g_Sound_Delete('SOUND_WEAPON_FIREBALL'); g_Sound_Delete('SOUND_WEAPON_EXPLODEBALL'); g_Sound_Delete('SOUND_WEAPON_FIREREV'); g_Sound_Delete('SOUND_PLAYER_JETFLY'); g_Sound_Delete('SOUND_PLAYER_JETON'); g_Sound_Delete('SOUND_PLAYER_JETOFF'); g_Sound_Delete('SOUND_PLAYER_CASING1'); g_Sound_Delete('SOUND_PLAYER_CASING2'); g_Sound_Delete('SOUND_PLAYER_SHELL1'); g_Sound_Delete('SOUND_PLAYER_SHELL2'); g_Texture_Delete('TEXTURE_WEAPON_ROCKET'); g_Frames_DeleteByName('FRAMES_WEAPON_BFG'); g_Frames_DeleteByName('FRAMES_WEAPON_PLASMA'); g_Frames_DeleteByName('FRAMES_WEAPON_IMPFIRE'); g_Frames_DeleteByName('FRAMES_WEAPON_BSPFIRE'); g_Frames_DeleteByName('FRAMES_WEAPON_CACOFIRE'); g_Frames_DeleteByName('FRAMES_WEAPON_MANCUBFIRE'); g_Frames_DeleteByName('FRAMES_EXPLODE_ROCKET'); g_Frames_DeleteByName('FRAMES_EXPLODE_BFG'); g_Frames_DeleteByName('FRAMES_EXPLODE_IMPFIRE'); g_Frames_DeleteByName('FRAMES_BFGHIT'); g_Frames_DeleteByName('FRAMES_FIRE'); g_Frames_DeleteByName('FRAMES_EXPLODE_PLASMA'); g_Frames_DeleteByName('FRAMES_EXPLODE_BSPFIRE'); g_Frames_DeleteByName('FRAMES_EXPLODE_CACOFIRE'); g_Frames_DeleteByName('FRAMES_SMOKE'); g_Frames_DeleteByName('FRAMES_WEAPON_BARONFIRE'); g_Frames_DeleteByName('FRAMES_EXPLODE_BARONFIRE'); end; procedure g_Weapon_gun(x, y, xd, yd, v, dmg: Integer; SpawnerUID: Word; CheckTrigger: Boolean); var a: Integer; x2, y2: Integer; dx, dy: Integer; xe, ye: Integer; xi, yi: Integer; s, c: Extended; //vx, vy: Integer; xx, yy, d: Integer; i: Integer; t1, _collide: Boolean; w, h: Word; begin a := GetAngle(x, y, xd, yd)+180; SinCos(DegToRad(-a), s, c); if Abs(s) < 0.01 then s := 0; if Abs(c) < 0.01 then c := 0; x2 := x+Round(c*gMapInfo.Width); y2 := y+Round(s*gMapInfo.Width); t1 := gWalls <> nil; _collide := False; w := gMapInfo.Width; h := gMapInfo.Height; xe := 0; ye := 0; dx := x2-x; dy := y2-y; if (xd = 0) and (yd = 0) then Exit; if dx > 0 then xi := 1 else if dx < 0 then xi := -1 else xi := 0; if dy > 0 then yi := 1 else if dy < 0 then yi := -1 else yi := 0; dx := Abs(dx); dy := Abs(dy); if dx > dy then d := dx else d := dy; //blood vel, for Monster.Damage() //vx := (dx*10 div d)*xi; //vy := (dy*10 div d)*yi; xx := x; yy := y; for i := 1 to d do begin xe := xe+dx; ye := ye+dy; if xe > d then begin xe := xe-d; xx := xx+xi; end; if ye > d then begin ye := ye-d; yy := yy+yi; end; if (yy > h) or (yy < 0) then Break; if (xx > w) or (xx < 0) then Break; if t1 then if ByteBool(gCollideMap[yy, xx] and MARK_BLOCKED) then begin _collide := True; g_GFX_Spark(xx-xi, yy-yi, 2+Random(2), 180+a, 0, 0); if g_Game_IsServer and g_Game_IsNet then MH_SEND_Effect(xx-xi, yy-yi, 180+a, NET_GFX_SPARK); end; if not _collide then _collide := GunHit(xx, yy, xi*v, yi*v, dmg, SpawnerUID, v <> 0) <> 0; if _collide then Break; end; if CheckTrigger and g_Game_IsServer then g_Triggers_PressL(X, Y, xx-xi, yy-yi, SpawnerUID, ACTIVATE_SHOT); end; procedure g_Weapon_punch(x, y: Integer; d, SpawnerUID: Word); var obj: TObj; begin obj.X := X; obj.Y := Y; obj.rect.X := 0; obj.rect.Y := 0; obj.rect.Width := 39; obj.rect.Height := 52; obj.Vel.X := 0; obj.Vel.Y := 0; obj.Accel.X := 0; obj.Accel.Y := 0; if g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME) <> 0 then g_Sound_PlayExAt('SOUND_WEAPON_HITPUNCH', x, y) else g_Sound_PlayExAt('SOUND_WEAPON_MISSPUNCH', x, y); end; function g_Weapon_chainsaw(x, y: Integer; d, SpawnerUID: Word): Integer; var obj: TObj; begin obj.X := X; obj.Y := Y; obj.rect.X := 0; obj.rect.Y := 0; obj.rect.Width := 32; obj.rect.Height := 52; obj.Vel.X := 0; obj.Vel.Y := 0; obj.Accel.X := 0; obj.Accel.Y := 0; Result := g_Weapon_Hit(@obj, d, SpawnerUID, HIT_SOME); end; procedure g_Weapon_rocket(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var find_id: DWORD; dx, dy: Integer; begin if WID < 0 then find_id := FindShot() else begin find_id := WID; if Integer(find_id) >= High(Shots) then SetLength(Shots, find_id + 64) end; with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := SHOT_ROCKETLAUNCHER_WIDTH; Obj.Rect.Height := SHOT_ROCKETLAUNCHER_HEIGHT; dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12); Animation := nil; triggers := nil; ShotType := WEAPON_ROCKETLAUNCHER; g_Texture_Get('TEXTURE_WEAPON_ROCKET', TextureID); end; Shots[find_id].SpawnerUID := SpawnerUID; if not Silent then g_Sound_PlayExAt('SOUND_WEAPON_FIREROCKET', x, y); end; procedure g_Weapon_revf(x, y, xd, yd: Integer; SpawnerUID, TargetUID: Word; WID: Integer = -1; Silent: Boolean = False); var find_id, FramesID: DWORD; dx, dy: Integer; begin if WID < 0 then find_id := FindShot() else begin find_id := WID; if Integer(find_id) >= High(Shots) then SetLength(Shots, find_id + 64) end; with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := SHOT_SKELFIRE_WIDTH; Obj.Rect.Height := SHOT_SKELFIRE_HEIGHT; dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 12); triggers := nil; ShotType := WEAPON_SKEL_FIRE; target := TargetUID; g_Frames_Get(FramesID, 'FRAMES_WEAPON_SKELFIRE'); Animation := TAnimation.Create(FramesID, True, 5); end; Shots[find_id].SpawnerUID := SpawnerUID; if not Silent then g_Sound_PlayExAt('SOUND_WEAPON_FIREREV', x, y); end; procedure g_Weapon_plasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var find_id, FramesID: DWORD; dx, dy: Integer; begin if WID < 0 then find_id := FindShot() else begin find_id := WID; if Integer(find_id) >= High(Shots) then SetLength(Shots, find_id + 64); end; with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := SHOT_PLASMA_WIDTH; Obj.Rect.Height := SHOT_PLASMA_HEIGHT; dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; ShotType := WEAPON_PLASMA; g_Frames_Get(FramesID, 'FRAMES_WEAPON_PLASMA'); Animation := TAnimation.Create(FramesID, True, 5); end; Shots[find_id].SpawnerUID := SpawnerUID; if not Silent then g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y); end; procedure g_Weapon_ball1(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var find_id, FramesID: DWORD; dx, dy: Integer; begin if WID < 0 then find_id := FindShot() else begin find_id := WID; if Integer(find_id) >= High(Shots) then SetLength(Shots, find_id + 64) end; with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := 16; Obj.Rect.Height := 16; dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; ShotType := WEAPON_IMP_FIRE; g_Frames_Get(FramesID, 'FRAMES_WEAPON_IMPFIRE'); Animation := TAnimation.Create(FramesID, True, 4); end; Shots[find_id].SpawnerUID := SpawnerUID; if not Silent then g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y); end; procedure g_Weapon_ball2(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var find_id, FramesID: DWORD; dx, dy: Integer; begin if WID < 0 then find_id := FindShot() else begin find_id := WID; if Integer(find_id) >= High(Shots) then SetLength(Shots, find_id + 64) end; with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := 16; Obj.Rect.Height := 16; dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; ShotType := WEAPON_CACO_FIRE; g_Frames_Get(FramesID, 'FRAMES_WEAPON_CACOFIRE'); Animation := TAnimation.Create(FramesID, True, 4); end; Shots[find_id].SpawnerUID := SpawnerUID; if not Silent then g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y); end; procedure g_Weapon_ball7(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var find_id, FramesID: DWORD; dx, dy: Integer; begin if WID < 0 then find_id := FindShot() else begin find_id := WID; if Integer(find_id) >= High(Shots) then SetLength(Shots, find_id + 64) end; with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := 32; Obj.Rect.Height := 16; dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; ShotType := WEAPON_BARON_FIRE; g_Frames_Get(FramesID, 'FRAMES_WEAPON_BARONFIRE'); Animation := TAnimation.Create(FramesID, True, 4); end; Shots[find_id].SpawnerUID := SpawnerUID; if not Silent then g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y); end; procedure g_Weapon_aplasma(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var find_id, FramesID: DWORD; dx, dy: Integer; begin if WID < 0 then find_id := FindShot() else begin find_id := WID; if Integer(find_id) >= High(Shots) then SetLength(Shots, find_id + 64) end; with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := 16; Obj.Rect.Height := 16; dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; ShotType := WEAPON_BSP_FIRE; g_Frames_Get(FramesID, 'FRAMES_WEAPON_BSPFIRE'); Animation := TAnimation.Create(FramesID, True, 4); end; Shots[find_id].SpawnerUID := SpawnerUID; if not Silent then g_Sound_PlayExAt('SOUND_WEAPON_FIREPLASMA', x, y); end; procedure g_Weapon_manfire(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var find_id, FramesID: DWORD; dx, dy: Integer; begin if WID < 0 then find_id := FindShot() else begin find_id := WID; if Integer(find_id) >= High(Shots) then SetLength(Shots, find_id + 64) end; with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := 32; Obj.Rect.Height := 32; dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; ShotType := WEAPON_MANCUB_FIRE; g_Frames_Get(FramesID, 'FRAMES_WEAPON_MANCUBFIRE'); Animation := TAnimation.Create(FramesID, True, 4); end; Shots[find_id].SpawnerUID := SpawnerUID; if not Silent then g_Sound_PlayExAt('SOUND_WEAPON_FIREBALL', x, y); end; procedure g_Weapon_bfgshot(x, y, xd, yd: Integer; SpawnerUID: Word; WID: Integer = -1; Silent: Boolean = False); var find_id, FramesID: DWORD; dx, dy: Integer; begin if WID < 0 then find_id := FindShot() else begin find_id := WID; if Integer(find_id) >= High(Shots) then SetLength(Shots, find_id + 64) end; with Shots[find_id] do begin g_Obj_Init(@Obj); Obj.Rect.Width := SHOT_BFG_WIDTH; Obj.Rect.Height := SHOT_BFG_HEIGHT; dx := IfThen(xd>x, -Obj.Rect.Width, 0); dy := -(Obj.Rect.Height div 2); throw(find_id, x+dx, y+dy, xd+dx, yd+dy, 16); triggers := nil; ShotType := WEAPON_BFG; g_Frames_Get(FramesID, 'FRAMES_WEAPON_BFG'); Animation := TAnimation.Create(FramesID, True, 6); end; Shots[find_id].SpawnerUID := SpawnerUID; if not Silent then g_Sound_PlayExAt('SOUND_WEAPON_FIREBFG', x, y); end; procedure g_Weapon_bfghit(x, y: Integer); var ID: DWORD; Anim: TAnimation; begin if g_Frames_Get(ID, 'FRAMES_BFGHIT') then begin Anim := TAnimation.Create(ID, False, 4); g_GFX_OnceAnim(x-32, y-32, Anim); Anim.Free(); end; end; procedure g_Weapon_pistol(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False); begin if not Silent then g_Sound_PlayExAt('SOUND_WEAPON_FIREPISTOL', x, y); g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True); if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then begin g_Weapon_gun(x, y+1, xd, yd+1, 1, 3, SpawnerUID, False); g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False); end; end; procedure g_Weapon_mgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False); begin if not Silent then if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRECGUN', x, y); g_Weapon_gun(x, y, xd, yd, 1, 3, SpawnerUID, True); if (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) and (g_GetUIDType(SpawnerUID) = UID_PLAYER) then begin g_Weapon_gun(x, y+1, xd, yd+1, 1, 2, SpawnerUID, False); g_Weapon_gun(x, y-1, xd, yd-1, 1, 2, SpawnerUID, False); end; end; procedure g_Weapon_shotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False); var i, j: Integer; begin if not Silent then if gSoundEffectsDF then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN', x, y); for i := 0 to 9 do begin j := Random(17)-8; // -8 .. 8 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 2 <> 0, 1, 0), 3, SpawnerUID, i=0); end; end; procedure g_Weapon_dshotgun(x, y, xd, yd: Integer; SpawnerUID: Word; Silent: Boolean = False); var a, i, j: Integer; begin if not Silent then g_Sound_PlayExAt('SOUND_WEAPON_FIRESHOTGUN2', x, y); if gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF] then a := 25 else a := 20; for i := 0 to a do begin j := Random(41)-20; // -20 .. 20 g_Weapon_gun(x, y+j, xd, yd+j, IfThen(i mod 3 <> 0, 0, 1), 3, SpawnerUID, i=0); end; end; procedure g_Weapon_Update(); var i, a, h, cx, cy, oldvx, oldvy: Integer; _id: DWORD; Anim: TAnimation; t: DWArray; st: Word; s: String; o: TObj; spl: Boolean; Loud: Boolean; begin if Shots = nil then Exit; for i := 0 to High(Shots) do begin if Shots[i].ShotType = 0 then Continue; Loud := True; with Shots[i] do begin Timeout := Timeout - 1; oldvx := Obj.Vel.X; oldvy := Obj.Vel.Y; // Активировать триггеры по пути (кроме уже активированных): if g_Game_IsServer then t := g_Triggers_PressR(Obj.X, Obj.Y, Obj.Rect.Width, Obj.Rect.Height, SpawnerUID, ACTIVATE_SHOT, triggers) else t := nil; if t <> nil then begin // Пополняем список активированных триггеров: if triggers = nil then triggers := t else begin h := High(t); for a := 0 to h do if not InDWArray(t[a], triggers) then begin SetLength(triggers, Length(triggers)+1); triggers[High(triggers)] := t[a]; end; end; end; // Анимация снаряда: if Animation <> nil then Animation.Update(); // Движение: spl := (ShotType <> WEAPON_PLASMA) and (ShotType <> WEAPON_BFG) and (ShotType <> WEAPON_BSP_FIRE); st := g_Obj_Move(@Obj, False, spl); if WordBool(st and MOVE_FALLOUT) or (Obj.X < -1000) or (Obj.X > gMapInfo.Width+1000) or (Obj.Y < -1000) then begin // На клиенте скорее всего и так уже выпал. ShotType := 0; Animation.Free(); Continue; end; cx := Obj.X + (Obj.Rect.Width div 2); cy := Obj.Y + (Obj.Rect.Height div 2); case ShotType of WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ракеты и снаряды Скелета begin // Вылетела из воды: if WordBool(st and MOVE_HITAIR) then g_Obj_SetSpeed(@Obj, 12); // В воде шлейф - пузыри, в воздухе шлейф - дым: if WordBool(st and MOVE_INWATER) then g_GFX_Bubbles(Obj.X+(Obj.Rect.Width div 2), Obj.Y+(Obj.Rect.Height div 2), 1+Random(3), 16, 16) else if g_Frames_Get(_id, 'FRAMES_SMOKE') then begin Anim := TAnimation.Create(_id, False, 3); Anim.Alpha := 150; g_GFX_OnceAnim(Obj.X-8+Random(9), Obj.Y+(Obj.Rect.Height div 2)-20+Random(9), Anim, ONCEANIM_SMOKE); Anim.Free(); end; // Попали в кого-то или в стену: if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or (g_Weapon_Hit(@Obj, 10, SpawnerUID, HIT_SOME, False) <> 0) or (Timeout < 1) then begin Obj.Vel.X := 0; Obj.Vel.Y := 0; g_Weapon_Explode(cx, cy, 60, SpawnerUID); if ShotType = WEAPON_SKEL_FIRE then begin // Взрыв снаряда Скелета if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then begin Anim := TAnimation.Create(TextureID, False, 8); Anim.Blending := False; g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim); Anim.Free(); end; end else begin // Взрыв Ракеты if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then begin Anim := TAnimation.Create(TextureID, False, 6); Anim.Blending := False; g_GFX_OnceAnim(cx-64, cy-64, Anim); Anim.Free(); end; end; g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y); ShotType := 0; end; if ShotType = WEAPON_SKEL_FIRE then begin // Самонаводка снаряда Скелета: if GetPos(target, @o) then throw(i, Obj.X, Obj.Y, o.X+o.Rect.X+(o.Rect.Width div 2)+o.Vel.X+o.Accel.X, o.Y+o.Rect.Y+(o.Rect.Height div 2)+o.Vel.Y+o.Accel.Y, 12); end; end; WEAPON_PLASMA, WEAPON_BSP_FIRE: // Плазма, плазма Арахнатрона begin // Попала в воду - электрошок по воде: if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then begin g_Sound_PlayExAt('SOUND_WEAPON_PLASMAWATER', Obj.X, Obj.Y); if g_Game_IsServer then CheckTrap(i, 10, HIT_ELECTRO); ShotType := 0; Continue; end; // Величина урона: if (ShotType = WEAPON_PLASMA) and (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF]) then a := 10 else a := 5; if ShotType = WEAPON_BSP_FIRE then a := 10; // Попали в кого-то или в стену: if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME, False) <> 0) or (Timeout < 1) then begin if ShotType = WEAPON_PLASMA then s := 'FRAMES_EXPLODE_PLASMA' else s := 'FRAMES_EXPLODE_BSPFIRE'; // Взрыв Плазмы: if g_Frames_Get(TextureID, s) then begin Anim := TAnimation.Create(TextureID, False, 3); Anim.Blending := False; g_GFX_OnceAnim(cx-16, cy-16, Anim); Anim.Free(); end; g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y); ShotType := 0; end; end; WEAPON_BFG: // BFG begin // Попала в воду - электрошок по воде: if WordBool(st and (MOVE_INWATER or MOVE_HITWATER)) then begin g_Sound_PlayExAt('SOUND_WEAPON_BFGWATER', Obj.X, Obj.Y); if g_Game_IsServer then CheckTrap(i, 1000, HIT_ELECTRO); ShotType := 0; Continue; end; // Попали в кого-то или в стену: if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or (g_Weapon_Hit(@Obj, SHOT_BFG_DAMAGE, SpawnerUID, HIT_BFG, False) <> 0) or (Timeout < 1) then begin // Лучи BFG: if g_Game_IsServer then g_Weapon_BFG9000(cx, cy, SpawnerUID); // Взрыв BFG: if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') then begin Anim := TAnimation.Create(TextureID, False, 6); Anim.Blending := False; g_GFX_OnceAnim(cx-64, cy-64, Anim); Anim.Free(); end; g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y); ShotType := 0; end; end; WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Выстрелы Беса, Какодемона Рыцаря/Барона ада begin // Вылетел из воды: if WordBool(st and MOVE_HITAIR) then g_Obj_SetSpeed(@Obj, 16); // Величина урона: if ShotType = WEAPON_IMP_FIRE then a := 5 else if ShotType = WEAPON_CACO_FIRE then a := 20 else a := 40; // Попали в кого-то или в стену: if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or (g_Weapon_Hit(@Obj, a, SpawnerUID, HIT_SOME) <> 0) or (Timeout < 1) then begin if ShotType = WEAPON_IMP_FIRE then s := 'FRAMES_EXPLODE_IMPFIRE' else if ShotType = WEAPON_CACO_FIRE then s := 'FRAMES_EXPLODE_CACOFIRE' else s := 'FRAMES_EXPLODE_BARONFIRE'; // Взрыв: if g_Frames_Get(TextureID, s) then begin Anim := TAnimation.Create(TextureID, False, 6); Anim.Blending := False; g_GFX_OnceAnim(cx-32, cy-32, Anim); Anim.Free(); end; g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y); ShotType := 0; end; end; WEAPON_MANCUB_FIRE: // Выстрел Манкубуса begin // Вылетел из воды: if WordBool(st and MOVE_HITAIR) then g_Obj_SetSpeed(@Obj, 16); // Попали в кого-то или в стену: if WordBool(st and (MOVE_HITWALL or MOVE_HITLAND or MOVE_HITCEIL)) or (g_Weapon_Hit(@Obj, 40, SpawnerUID, HIT_SOME, False) <> 0) or (Timeout < 1) then begin // Взрыв: if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then begin Anim := TAnimation.Create(TextureID, False, 6); Anim.Blending := False; g_GFX_OnceAnim(cx-64, cy-64, Anim); Anim.Free(); end; g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y); ShotType := 0; end; end; end; // case ShotType of... // Если снаряда уже нет, удаляем анимацию: if (ShotType = 0) then begin if gGameSettings.GameType = GT_SERVER then MH_SEND_DeleteShot(i, Obj.X, Obj.Y, Loud); Animation.Free(); Animation := nil; end else if (oldvx <> Obj.Vel.X) or (oldvy <> Obj.Vel.Y) then if gGameSettings.GameType = GT_SERVER then MH_SEND_UpdateShot(i); end; end; end; procedure g_Weapon_Draw(); var i: Integer; a: SmallInt; p: TPoint; begin if Shots = nil then Exit; for i := 0 to High(Shots) do if Shots[i].ShotType <> 0 then with Shots[i] do begin if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) or (Shots[i].ShotType = WEAPON_BARON_FIRE) or (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or (Shots[i].ShotType = WEAPON_SKEL_FIRE) then a := -GetAngle2(Obj.Vel.X, Obj.Vel.Y) else a := 0; p.X := Obj.Rect.Width div 2; p.Y := Obj.Rect.Height div 2; if Animation <> nil then begin if (Shots[i].ShotType = WEAPON_BARON_FIRE) or (Shots[i].ShotType = WEAPON_MANCUB_FIRE) or (Shots[i].ShotType = WEAPON_SKEL_FIRE) then Animation.DrawEx(Obj.X, Obj.Y, M_NONE, p, a) else Animation.Draw(Obj.X, Obj.Y, M_NONE); end else begin if (Shots[i].ShotType = WEAPON_ROCKETLAUNCHER) then e_DrawAdv(TextureID, Obj.X, Obj.Y, 0, True, False, a, @p, M_NONE) else e_Draw(TextureID, Obj.X, Obj.Y, 0, True, False); end; if g_debug_Frames then begin e_DrawQuad(Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.X+Obj.Rect.X+Obj.Rect.Width-1, Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1, 0, 255, 0); end; end; end; function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean; var a: Integer; begin Result := False; if Shots = nil then Exit; for a := 0 to High(Shots) do if (Shots[a].ShotType <> 0) and (Shots[a].SpawnerUID <> UID) then if ((Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X > 0) and (Shots[a].Obj.X < X)) or (Shots[a].Obj.Vel.Y = 0) and (Shots[a].Obj.Vel.X < 0) and (Shots[a].Obj.X > X) then if (Abs(X-Shots[a].Obj.X) < Abs(Shots[a].Obj.Vel.X*Time)) and g_Collide(X, Y, Width, Height, X, Shots[a].Obj.Y, Shots[a].Obj.Rect.Width, Shots[a].Obj.Rect.Height) and g_TraceVector(X, Y, Shots[a].Obj.X, Shots[a].Obj.Y) then begin Result := True; Exit; end; end; procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter); var count, i, j: Integer; dw: DWORD; begin // Считаем количество существующих снарядов: count := 0; if Shots <> nil then for i := 0 to High(Shots) do if Shots[i].ShotType <> 0 then count := count + 1; Mem := TBinMemoryWriter.Create((count+1) * 80); // Количество снарядов: Mem.WriteInt(count); if count = 0 then Exit; for i := 0 to High(Shots) do if Shots[i].ShotType <> 0 then begin // Сигнатура снаряда: dw := SHOT_SIGNATURE; // 'SHOT' Mem.WriteDWORD(dw); // Тип снаряда: Mem.WriteByte(Shots[i].ShotType); // Цель: Mem.WriteWord(Shots[i].Target); // UID стрелявшего: Mem.WriteWord(Shots[i].SpawnerUID); // Размер поля Triggers: dw := Length(Shots[i].Triggers); Mem.WriteDWORD(dw); // Триггеры, активированные выстрелом: for j := 0 to Integer(dw)-1 do Mem.WriteDWORD(Shots[i].Triggers[j]); // Объект снаряда: Obj_SaveState(@Shots[i].Obj, Mem); end; end; procedure g_Weapon_LoadState(var Mem: TBinMemoryReader); var count, i, j: Integer; dw: DWORD; begin if Mem = nil then Exit; // Количество снарядов: Mem.ReadInt(count); SetLength(Shots, count); if count = 0 then Exit; for i := 0 to count-1 do begin // Сигнатура снаряда: Mem.ReadDWORD(dw); if dw <> SHOT_SIGNATURE then // 'SHOT' begin raise EBinSizeError.Create('g_Weapons_LoadState: Wrong Shot Signature'); end; // Тип снаряда: Mem.ReadByte(Shots[i].ShotType); // Цель: Mem.ReadWord(Shots[i].Target); // UID стрелявшего: Mem.ReadWord(Shots[i].SpawnerUID); // Размер поля Triggers: Mem.ReadDWORD(dw); SetLength(Shots[i].Triggers, dw); // Триггеры, активированные выстрелом: for j := 0 to Integer(dw)-1 do Mem.ReadDWORD(Shots[i].Triggers[j]); // Объект предмета: Obj_LoadState(@Shots[i].Obj, Mem); // Установка текстуры или анимации: Shots[i].TextureID := DWORD(-1); Shots[i].Animation := nil; case Shots[i].ShotType of WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: begin g_Texture_Get('TEXTURE_WEAPON_ROCKET', Shots[i].TextureID); end; WEAPON_PLASMA: begin g_Frames_Get(dw, 'FRAMES_WEAPON_PLASMA'); Shots[i].Animation := TAnimation.Create(dw, True, 5); end; WEAPON_BFG: begin g_Frames_Get(dw, 'FRAMES_WEAPON_BFG'); Shots[i].Animation := TAnimation.Create(dw, True, 6); end; WEAPON_IMP_FIRE: begin g_Frames_Get(dw, 'FRAMES_WEAPON_IMPFIRE'); Shots[i].Animation := TAnimation.Create(dw, True, 4); end; WEAPON_BSP_FIRE: begin g_Frames_Get(dw, 'FRAMES_WEAPON_BSPFIRE'); Shots[i].Animation := TAnimation.Create(dw, True, 4); end; WEAPON_CACO_FIRE: begin g_Frames_Get(dw, 'FRAMES_WEAPON_CACOFIRE'); Shots[i].Animation := TAnimation.Create(dw, True, 4); end; WEAPON_BARON_FIRE: begin g_Frames_Get(dw, 'FRAMES_WEAPON_BARONFIRE'); Shots[i].Animation := TAnimation.Create(dw, True, 4); end; WEAPON_MANCUB_FIRE: begin g_Frames_Get(dw, 'FRAMES_WEAPON_MANCUBFIRE'); Shots[i].Animation := TAnimation.Create(dw, True, 4); end; end; end; end; procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True); var cx, cy: Integer; Anim: TAnimation; s: string; begin if Shots = nil then Exit; if (I > High(Shots)) or (I < 0) then Exit; with Shots[I] do begin if ShotType = 0 then Exit; Obj.X := X; Obj.Y := Y; cx := Obj.X + (Obj.Rect.Width div 2); cy := Obj.Y + (Obj.Rect.Height div 2); case ShotType of WEAPON_ROCKETLAUNCHER, WEAPON_SKEL_FIRE: // Ракеты и снаряды Скелета begin if Loud then begin if ShotType = WEAPON_SKEL_FIRE then begin // Взрыв снаряда Скелета if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_SKELFIRE') then begin Anim := TAnimation.Create(TextureID, False, 8); Anim.Blending := False; g_GFX_OnceAnim((Obj.X+32)-32, (Obj.Y+8)-32, Anim); Anim.Free(); end; end else begin // Взрыв Ракеты if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') then begin Anim := TAnimation.Create(TextureID, False, 6); Anim.Blending := False; g_GFX_OnceAnim(cx-64, cy-64, Anim); Anim.Free(); end; end; g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', Obj.X, Obj.Y); end; end; WEAPON_PLASMA, WEAPON_BSP_FIRE: // Плазма, плазма Арахнатрона begin if ShotType = WEAPON_PLASMA then s := 'FRAMES_EXPLODE_PLASMA' else s := 'FRAMES_EXPLODE_BSPFIRE'; if g_Frames_Get(TextureID, s) and loud then begin Anim := TAnimation.Create(TextureID, False, 3); Anim.Blending := False; g_GFX_OnceAnim(cx-16, cy-16, Anim); Anim.Free(); g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEPLASMA', Obj.X, Obj.Y); end; end; WEAPON_BFG: // BFG begin // Взрыв BFG: if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_BFG') and Loud then begin Anim := TAnimation.Create(TextureID, False, 6); Anim.Blending := False; g_GFX_OnceAnim(cx-64, cy-64, Anim); Anim.Free(); g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', Obj.X, Obj.Y); end; end; WEAPON_IMP_FIRE, WEAPON_CACO_FIRE, WEAPON_BARON_FIRE: // Выстрелы Беса, Какодемона Рыцаря/Барона ада begin if ShotType = WEAPON_IMP_FIRE then s := 'FRAMES_EXPLODE_IMPFIRE' else if ShotType = WEAPON_CACO_FIRE then s := 'FRAMES_EXPLODE_CACOFIRE' else s := 'FRAMES_EXPLODE_BARONFIRE'; if g_Frames_Get(TextureID, s) and Loud then begin Anim := TAnimation.Create(TextureID, False, 6); Anim.Blending := False; g_GFX_OnceAnim(cx-32, cy-32, Anim); Anim.Free(); g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y); end; end; WEAPON_MANCUB_FIRE: // Выстрел Манкубуса begin if g_Frames_Get(TextureID, 'FRAMES_EXPLODE_ROCKET') and Loud then begin Anim := TAnimation.Create(TextureID, False, 6); Anim.Blending := False; g_GFX_OnceAnim(cx-64, cy-64, Anim); Anim.Free(); g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBALL', Obj.X, Obj.Y); end; end; end; // case ShotType of... ShotType := 0; Animation.Free(); end; end; end.