Created
June 16, 2019 22:33
-
-
Save aemarkov/5b5352d3b8deed87023996c28186081b to your computer and use it in GitHub Desktop.
E2 script for Garry's Mod killer bath
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is an advanced KILLER BATH | |
# Features: | |
# - precise player targeting with PID-controller | |
# - rotation control | |
# - OPTIONAL: teleport though obstacles (using ranger) | |
# - OPTIONAL: explode when close to the player | |
# (to kill players with no-clip) | |
@name killer_bath | |
@inputs | |
@outputs Detonate Arm TeleportPos:vector Jump Freeze | |
@persist MovePID:table RotPID:table LastTarget:entity PrevDist MyEntities:array FreezeTimeout | |
@trigger | |
interval(5) | |
Base = entity():isWeldedTo() | |
IS_TARGET_OWNER = 1 # Bath will target owner | |
IS_DETONATE = 0 # Bath will detonate when close to player | |
IS_TELEPORT = 0 # Bath will teleport through the obstacles | |
IS_BEHIND_THE_BACK = 0 # Bath will hide behind the player's back instead of killing him | |
IS_CRYING_ANGEL = 0 # Bath will freeze when any player look at it | |
ARM_DIST = 1000 | |
DETONATE_DIST = 300 | |
JUMP_MIN_DIST = 10 | |
JUMP_MIN_DIST_ERROR = 40 | |
BEHIND_THE_BACK_DIST = 100 | |
MIN_ROTATE_DIST = 0 | |
MAX_FREEZE_TIMEOUT = 30 | |
####################### PID CONTROLLER ############################# | |
# Add nessesary items to PID's table | |
function initPID(PID:table, P:number, I:number, D:number) | |
{ | |
PID["PGain", number]=P | |
PID["IGain", number]=I | |
PID["DGain", number]=D | |
PID["IState", vector] = vec() | |
PID["DState", vector] = vec() | |
} | |
# Calculate given PID | |
function vector calcPID(Vec:vector, PID:table) | |
{ | |
PTerm = PID["PGain", number] * Vec | |
IState = PID["IState", vector] | |
IMax = PID["IMax", number] | |
IState = IState + Vec | |
if(IState:length() > IMax) | |
{ | |
IState = IState:normalized() * IMax | |
PID["IState", vector] = IState | |
} | |
ITerm = PID["IGain", number] * IState | |
DTerm = PID["DGain", number] * (Vec - PID["DState", vector]) | |
PID["DState", vector] = Vec | |
return PTerm + IState + DTerm | |
} | |
#################################################################### | |
# Find closest player | |
function entity findClosestPlayer() | |
{ | |
Players = players() | |
MyPos = Base:pos() | |
ClosestPlayer = noentity() | |
MinDist = 99999 | |
for(I = 1, Players:count()) | |
{ | |
Player = Players[I, entity] | |
PlayerPos = Player:pos() | |
Dist = (PlayerPos - MyPos):length() | |
NotOwner = (Player != owner()) || (IS_TARGET_OWNER == 1) | |
if(Dist < MinDist && Player:health() > 0 && NotOwner) | |
{ | |
MinDist = Dist | |
ClosestPlayer = Player | |
} | |
} | |
return ClosestPlayer | |
} | |
# Find closest player and update target | |
function entity chooseTarget() | |
{ | |
# Find player | |
ClosestPlayer = findClosestPlayer() | |
if(ClosestPlayer==noentity()) | |
{ | |
LastTarget = ClosestPlayer | |
return noentity() | |
} | |
if(ClosestPlayer != LastTarget) | |
{ | |
print("New target: " + ClosestPlayer:name()) | |
} | |
LastTarget = ClosestPlayer | |
return ClosestPlayer | |
} | |
#################################################################### | |
# Apply force to move | |
function move(TargetPos:vector, LookToPos:vector) | |
{ | |
#holoCreate(10, TargetPos) | |
Vec0 = TargetPos - Base:pos() | |
Base:applyForce(calcPID(Vec0, MovePID)) | |
if((LookToPos - Base:pos()):length() > MIN_ROTATE_DIST) | |
{ | |
Heading = Base:heading(LookToPos) | |
Angles = Base:angles() | |
RotErr = vec(-Base:angles():roll(), -Heading:pitch(), -Heading:yaw()) | |
Base:applyTorque(calcPID(RotErr, RotPID)) | |
} | |
} | |
# Check distance and detonate warhead | |
function detonate(Distance) | |
{ | |
Arm = Distance < ARM_DIST | |
if(Distance < DETONATE_DIST && Detonate == 0) | |
{ | |
Detonate = 1 | |
} | |
else | |
{ | |
Detonate = 0 | |
} | |
} | |
# Teleport if can't reach target | |
function teleport(TargetPos:vector) | |
{ | |
R = rangerOffset(Base:pos(), TargetPos) | |
#holoCreate(10, R:position()) | |
DistToHit = R:distance() | |
DistError = Dist - DistToHit | |
Dist = (TargetPos - Base:pos()):length() | |
if(Jump == 0 && Dist > JUMP_MIN_DIST && DistError > JUMP_MIN_DIST_ERROR && !entity():isFrozen() && !Base:isFrozen()) | |
{ | |
TeleportPos = TargetPos | |
Jump = 1 | |
print("Jump") | |
} | |
else | |
{ | |
Jump = 0 | |
} | |
#print(Dist, PrevDist-Dist) | |
#PrevDist = Dist | |
} | |
#################################################################### | |
# Return entities welded to Base | |
function array get_my_entities() | |
{ | |
I=1 | |
MyEnts = array() | |
MyEnts:pushEntity(Base) | |
while(1) | |
{ | |
Child = Base:isWeldedTo(I) | |
if(Child:model()=="") { break } | |
MyEnts:pushEntity(Child) | |
I = I+1 | |
} | |
return MyEnts | |
} | |
# Is any player looking at this | |
function number is_looking_at() | |
{ | |
Players = players() | |
for(I = 1, Players:count()) | |
{ | |
for(J = 1, MyEntities:count()) | |
{ | |
if(Players[I, entity]:aimEntity() == MyEntities[J, entity]) | |
{ | |
return 1 | |
} | |
} | |
} | |
return 0 | |
} | |
#################################################################### | |
# Initialize all | |
function setup() | |
{ | |
initPID(MovePID, 1000, 0, 10000) | |
initPID(RotPID, 5000, 0, 80000) | |
LastTarget = noentity() | |
MyEntities = get_my_entities() | |
rangerPersist(1) | |
rangerFilter(MyEntities) | |
} | |
# Run every cycle | |
function loop() | |
{ | |
# Crying angel mode - freezes when any player look at | |
# Bath moving very fast, so I add some delay to simplify | |
# It will stay freezed MAX_FREEZE_TIMEOUT cycles after last looking at | |
if(IS_CRYING_ANGEL == 1) | |
{ | |
Freeze = is_looking_at() | |
if(Freeze == 1) | |
{ | |
FreezeTimeout = 0 | |
} | |
else | |
{ | |
FreezeTimeout = FreezeTimeout+1 | |
} | |
if(FreezeTimeout < MAX_FREEZE_TIMEOUT) | |
{ | |
return | |
} | |
} | |
Target = chooseTarget() | |
if(Target == noentity()) | |
{ | |
return | |
} | |
TargetPos = Target:pos() + Target:boxCenter() | |
# Do not kill the player, hide behind his back | |
if(IS_BEHIND_THE_BACK == 1) | |
{ | |
Eye = Target:eye() | |
EyePos = TargetPos - vec(Eye:x(), Eye:y(), 0):normalized() * BEHIND_THE_BACK_DIST | |
LookToPos = TargetPos | |
TargetPos = EyePos | |
} | |
else | |
{ | |
LookToPos = TargetPos | |
} | |
move(TargetPos, LookToPos) | |
if(IS_TELEPORT) | |
{ | |
teleport(TargetPos) | |
} | |
if(IS_DETONATE) | |
{ | |
Dist = (TargetPos - Base:pos()):length() | |
detonate(Dist) | |
} | |
} | |
if(first() | duped()) | |
{ | |
setup() | |
} | |
else | |
{ | |
loop() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment