546 lines
16 KiB
Plaintext
546 lines
16 KiB
Plaintext
version "4.10"
|
|
|
|
const HDLD_KIRI_JUMPERCABLES = "jmp";
|
|
|
|
const jumperCablesRaycastRange = 48; // Same range as the DERP.
|
|
const jumperCablesFixRange = 48; // Maximum distance to a broken linedef to fix, from the raycast hit.
|
|
|
|
class JumperCablesMapLoader : EventHandler
|
|
{
|
|
Map<int, int> originalLinedefSpecials2;
|
|
Array<JumperCablesDeployed> deployedCables;
|
|
|
|
static JumperCablesMapLoader Get()
|
|
{
|
|
JumperCablesMapLoader loader;
|
|
loader = JumperCablesMapLoader(EventHandler.Find("JumperCablesMapLoader"));
|
|
return loader;
|
|
}
|
|
|
|
override void WorldLoaded(WorldEvent e)
|
|
{
|
|
JumperCablesMapLoader loader = Get();
|
|
|
|
// Clear out anything from the last level.
|
|
deployedCables.Clear();
|
|
loader.originalLinedefSpecials2.Clear();
|
|
|
|
// Go through all the linedefs and make note of what they're
|
|
// supposed to do.
|
|
for(int k = 0; k < level.Lines.size(); k++) {
|
|
Line line = level.Lines[k];
|
|
if(line.special != 0) {
|
|
loader.originalLinedefSpecials2.Insert(line.index(), line.special);
|
|
}
|
|
}
|
|
}
|
|
|
|
override void WorldLineActivated(WorldEvent e)
|
|
{
|
|
// TODO: Deplete batteries.
|
|
//Console.printf("Line activated: %d %d\n", e.ActivatedLine.index(), e.ActivatedLine.special);
|
|
|
|
for(int cableIndex = 0; cableIndex < deployedCables.size(); cableIndex++) {
|
|
if(deployedCables[cableIndex].lineDefIndex == e.ActivatedLine.index()) {
|
|
|
|
deployedCables[cableIndex].DecrementBattery();
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RegisterCables(JumperCablesDeployed cableActor)
|
|
{
|
|
Console.printf("REGISTERING %d\n", deployedCables.Find(cableActor));
|
|
if(deployedCables.Find(cableActor) == deployedCables.size()) {
|
|
deployedCables.Push(cableActor);
|
|
|
|
// FIXME: Remove this.
|
|
Console.printf("Cables registered %d\n", deployedCables.size());
|
|
}
|
|
}
|
|
|
|
void UnregisterCables(JumperCablesDeployed cableActor)
|
|
{
|
|
Console.printf("UNREGISTERING %d\n", deployedCables.Find(cableActor));
|
|
int index = deployedCables.find(cableActor);
|
|
if(index != deployedCables.size()) {
|
|
deployedCables.delete(index);
|
|
|
|
// FIXME: Remove this.
|
|
Console.printf("Cables unregistered %d\n", deployedCables.size());
|
|
}
|
|
}
|
|
}
|
|
|
|
class JumperCablesDeployed : HDUPK
|
|
{
|
|
int lineDefIndex;
|
|
int charges;
|
|
|
|
default
|
|
{
|
|
radius 2;
|
|
height 4;
|
|
+SpriteAngle;
|
|
Scale 0.4;
|
|
}
|
|
|
|
states
|
|
{
|
|
spawn:
|
|
JMPR CABD -1;
|
|
stop;
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
Console.printf("IS THIS EVEN GETTING CALLED!?\n");
|
|
bNoGravity = true;
|
|
|
|
// Register us with the global event handler so we can
|
|
// respond to the linedef getting used.
|
|
JumperCablesMapLoader loader = JumperCablesMapLoader.Get();
|
|
loader.RegisterCables(self);
|
|
|
|
UpdateSprite();
|
|
}
|
|
|
|
void SetWallSprite(Vector2 lineDir)
|
|
{
|
|
bFlatSprite = false;
|
|
bWallSprite = true;
|
|
A_SetAngle(atan2(lineDir) + 90);
|
|
}
|
|
|
|
void SetFlatSprite(float angle)
|
|
{
|
|
bFlatSprite = true;
|
|
bWallSprite = false;
|
|
A_SetAngle(angle);
|
|
}
|
|
|
|
void UpdateSprite()
|
|
{
|
|
// Charge levels correspond to battery sprite charge levels.
|
|
if(charges > 13) {
|
|
frame = 0;
|
|
} else if(charges > 6) {
|
|
frame = 1;
|
|
} else if(charges > 0) {
|
|
frame = 2;
|
|
} else {
|
|
frame = 3;
|
|
}
|
|
}
|
|
|
|
void DecrementBattery()
|
|
{
|
|
charges -= 1;
|
|
Console.printf("battery used, now: %d", charges);
|
|
if(charges <= 0) {
|
|
charges = 0;
|
|
DespawnToBattery();
|
|
}
|
|
|
|
UpdateSprite();
|
|
}
|
|
|
|
void DespawnToBattery()
|
|
{
|
|
// Re-break the linedef.
|
|
level.Lines[lineDefIndex].special = 0;
|
|
|
|
// Spawn a battery, with the same level of charge.
|
|
HDMagAmmo.SpawnMag(self, "HDBattery", self.charges);
|
|
|
|
// Spawn some smoke.
|
|
int smokeCount = random(1, 4);
|
|
float smokeVel = 20.0;
|
|
for(int k = 0; k < smokeCount; k++) {
|
|
A_SpawnItemEx(
|
|
"HDSmokeChunk",
|
|
0.0, 0.0, 0.0,
|
|
frandom(-smokeVel, smokeVel),
|
|
frandom(-smokeVel, smokeVel),
|
|
frandom(-smokeVel, smokeVel));
|
|
}
|
|
|
|
// Spawn a bunch of sparks.
|
|
int sparkAmount = random(3,6);
|
|
for(int k = 0; k < sparkAmount; k++) {
|
|
SpawnSpark();
|
|
}
|
|
|
|
JumperCablesMapLoader loader = JumperCablesMapLoader.Get();
|
|
loader.UnregisterCables(self);
|
|
|
|
Destroy();
|
|
}
|
|
|
|
override bool OnGrab(Actor grabber)
|
|
{
|
|
DespawnToBattery();
|
|
return false;
|
|
}
|
|
|
|
void SpawnSpark()
|
|
{
|
|
float sparkSpeed = 5.0;
|
|
float sparkOffset = 2.0;
|
|
A_StartSound("misc/arccrackle", CHAN_AUTO);
|
|
A_SpawnParticle(
|
|
"white",
|
|
SPF_RELATIVE | SPF_FULLBRIGHT,
|
|
random(1, 10), // lifetime
|
|
frandom(1, 7), // size
|
|
0, // angle
|
|
// offset...
|
|
frandom(-sparkOffset, sparkOffset),
|
|
frandom(-sparkOffset, sparkOffset),
|
|
frandom(-sparkOffset, sparkOffset),
|
|
// vel...
|
|
frandom(-sparkSpeed, sparkSpeed),
|
|
frandom(-sparkSpeed, sparkSpeed),
|
|
frandom(-sparkSpeed, sparkSpeed));
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
if(frandom(0.0, 1.0) < 0.02) {
|
|
SpawnSpark();
|
|
}
|
|
}
|
|
|
|
// TODO: Tick function (or whatever) where we set the sprite to indicate the charge level.
|
|
// TODO: Despawn if the sector gets broken *again*?
|
|
}
|
|
|
|
class JumperCablesUsable : HDWeapon
|
|
{
|
|
default
|
|
{
|
|
+weapon.wimpy_weapon;
|
|
+inventory.invbar;
|
|
+hdweapon.droptranslation;
|
|
+hdweapon.fitsinbackpack;
|
|
hdweapon.barrelsize 0,0,0;
|
|
weapon.selectionorder 1014;
|
|
|
|
scale 0.6;
|
|
inventory.icon "JMPKA0";
|
|
inventory.pickupmessage "Picked up a wiring bypass kit.";
|
|
inventory.pickupsound "derp/crawl";
|
|
translation 0;
|
|
tag "Wiring Bypass Kit";
|
|
|
|
hdweapon.refid HDLD_KIRI_JUMPERCABLES;
|
|
}
|
|
|
|
override double WeaponBulk()
|
|
{
|
|
return 7;
|
|
}
|
|
|
|
override bool AddSpareWeapon(actor newowner)
|
|
{
|
|
return AddSpareWeaponRegular(newowner);
|
|
}
|
|
|
|
override hdweapon GetSpareWeapon(actor newowner,bool reverse,bool doselect)
|
|
{
|
|
return GetSpareWeaponRegular(newowner,reverse,doselect);
|
|
}
|
|
|
|
|
|
states
|
|
{
|
|
|
|
spawn:
|
|
JMPK A -1;
|
|
stop;
|
|
|
|
select:
|
|
TNT1 A 0;
|
|
goto super::select;
|
|
|
|
ready:
|
|
TNT1 A 1 {
|
|
if(PressingFire()) {
|
|
SetWeaponState("deploy");
|
|
}
|
|
A_WeaponReady(WRF_NOFIRE | WRF_ALLOWUSER3); // USER3 = MagManager.
|
|
|
|
FLineTraceData linetraceData;
|
|
DoLineTrace(HDPlayerPawn(invoker.owner), linetraceData);
|
|
|
|
if(linetraceData.hitType != TRACE_HitNone) {
|
|
Vector3 hitPos = linetraceData.hitLocation - Vec3Offset(0, 0, 0);
|
|
SpawnParticleForLineTrace(
|
|
HDPlayerPawn(invoker.owner),
|
|
linetraceData);
|
|
}
|
|
}
|
|
goto readyend;
|
|
|
|
deploy:
|
|
TNT1 AA 1;
|
|
TNT1 AAAA 1;
|
|
TNT1 AAAA 1;
|
|
|
|
TNT1 A 0 A_JumpIf(!pressingfire(),"ready");
|
|
TNT1 A 4 A_StartSound("weapons/pismagclick",CHAN_WEAPON);
|
|
TNT1 A 2 A_StartSound("derp/crawl",CHAN_WEAPON,CHANF_OVERLAP);
|
|
|
|
TNT1 A 1 {
|
|
invoker.owner.A_Log("Blep");
|
|
A_WeaponReady(WRF_NOFIRE | WRF_ALLOWRELOAD | WRF_ALLOWUSER4);
|
|
AttachCables(HDPlayerPawn(invoker.owner));
|
|
}
|
|
goto ready;
|
|
|
|
user3:
|
|
#### A 0 A_MagManager("HDBattery");
|
|
goto ready;
|
|
|
|
}
|
|
|
|
action void SpawnParticleForLineTrace(HDPlayerPawn pawn, FLineTraceData linetraceData)
|
|
{
|
|
|
|
Line hitLine = linetraceData.hitLine;
|
|
String particleColor = "darkred";
|
|
|
|
// if(linetraceData.hitType == TRACE_HitWall) {
|
|
// JumperCablesMapLoader loader = JumperCablesMapLoader.Get();
|
|
// int originalSpecial = loader.originalLinedefSpecials2.Get(hitLine.index());
|
|
// int thisLineSpecial = hitLine.special;
|
|
|
|
// if(originalSpecial != thisLineSpecial) {
|
|
// particleColor = "green";
|
|
// }
|
|
// }
|
|
|
|
if(LineTraceFindbrokenLine(linetraceData)) {
|
|
particleColor = "green";
|
|
}
|
|
|
|
Vector3 hitPos = linetraceData.hitLocation - Vec3Offset(0, 0, 0);
|
|
// console.printf("hit pos: %f %f %f\n", hitPos.x, hitPos.y, hitPos.z);
|
|
A_SpawnParticle(
|
|
particleColor,
|
|
0, 10, 2, 0,
|
|
hitPos.x,
|
|
hitPos.y,
|
|
hitPos.z);
|
|
}
|
|
|
|
action float Vec2Mag(Vector2 v)
|
|
{
|
|
return sqrt(v.x * v.x + v.y * v.y);
|
|
}
|
|
|
|
action float GetDistanceToLine(Line testLine, Vector3 position)
|
|
{
|
|
Vector2 pos2d;
|
|
pos2d.x = position.x;
|
|
pos2d.y = position.y;
|
|
|
|
Vector2 origin = testLine.v1.p;
|
|
|
|
// Get the normalized direction from v1 to v2.
|
|
Vector2 direction = testLine.v2.p - origin;
|
|
float direction_magnitude = Vec2Mag(direction);
|
|
direction = direction / direction_magnitude;
|
|
|
|
// Get the position relative to the origin.
|
|
pos2d -= origin;
|
|
|
|
// Project pos2d onto direction, to get a position along the line.
|
|
double t = direction dot pos2d;
|
|
|
|
// Clamp position along line to start/end.
|
|
if(t < 0.0) {
|
|
t = 0;
|
|
}
|
|
if(t > direction_magnitude) {
|
|
t = direction_magnitude;
|
|
}
|
|
|
|
// Closest point on the line (as an offset from origin).
|
|
Vector2 position_on_line = direction * t;
|
|
|
|
Vector2 delta_to_line = position_on_line - pos2d;
|
|
float final_distance = Vec2Mag(delta_to_line);
|
|
|
|
return final_distance;
|
|
}
|
|
|
|
action void DoLineTrace(HDPlayerPawn pawn, FLineTraceData linetraceData)
|
|
{
|
|
pawn.LineTrace(
|
|
pawn.angle,
|
|
48,
|
|
pawn.pitch,
|
|
flags : TRF_THRUACTORS,
|
|
offsetz : pawn.height * 0.8,
|
|
data : linetraceData);
|
|
}
|
|
|
|
action Line LineTraceFindbrokenLine(FLineTraceData linetraceData)
|
|
{
|
|
JumperCablesMapLoader loader = JumperCablesMapLoader.Get();
|
|
|
|
// If we're placing this on a wall, then we'll just look at the wall's special.
|
|
if(linetraceData.hitType == TRACE_HitWall) {
|
|
|
|
if(linetraceData.hitLine) {
|
|
|
|
Line hitLine = linetraceData.hitLine;
|
|
|
|
if(loader.originalLinedefSpecials2.CheckKey(hitLine.index())) {
|
|
|
|
int originalSpecial = loader.originalLinedefSpecials2.Get(hitLine.index());
|
|
int currentSpecial = hitLine.special;
|
|
|
|
if(originalSpecial != currentSpecial)
|
|
{
|
|
return hitLine;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we hit a sector *or* got close enough to another linedef
|
|
// attached to the sector that we're in, then let's find the
|
|
// closest linedef within range that we know is damaged.
|
|
if(linetraceData.hitType == TRACE_HitFloor ||
|
|
linetraceData.hitType == TRACE_HitCeiling ||
|
|
linetraceData.hitType == TRACE_HitWall)
|
|
{
|
|
Sector sec = linetraceData.hitSector;
|
|
Line closestLineInRange = null;
|
|
float closestLineDistance = jumperCablesFixRange;
|
|
|
|
// Iterate through all linedefs connected to the sector.
|
|
for(int secLineIndex = 0; secLineIndex < sec.lines.size(); secLineIndex++) {
|
|
|
|
Line secLine = sec.lines[secLineIndex];
|
|
|
|
// If this is currently special, we don't care about it.
|
|
if(secLine.special == 0) {
|
|
|
|
// If this *used* to be special, we DO care about it!
|
|
if(loader.originalLinedefSpecials2.CheckKey(secLine.index())) {
|
|
|
|
// See if thise is the closest one we've found so far.
|
|
float distanceToLine = GetDistanceToLine(
|
|
secLine, linetraceData.hitLocation);
|
|
if(distanceToLine <= closestLineDistance) {
|
|
closestLineInRange = secLine;
|
|
closestLineDistance = distanceToLine;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we found something broken, then return the closest
|
|
// thing we found.
|
|
if(closestLineInRange) {
|
|
Console.printf(
|
|
"Dist to line: %f\n",
|
|
GetDistanceToLine(
|
|
closestLineInRange, linetraceData.hitLocation));
|
|
return closestLineInRange;
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
action void AttachCables(HDPlayerPawn pawn)
|
|
{
|
|
HDMagAmmo batteryAmmo = HDMagAmmo(FindInventory("HDBattery"));
|
|
|
|
if(!batteryAmmo || batteryAmmo.amount < 1) {
|
|
invoker.owner.A_Log("You need a battery to use this.");
|
|
return;
|
|
}
|
|
|
|
JumperCablesMapLoader loader = JumperCablesMapLoader.Get();
|
|
FLineTraceData linetraceData;
|
|
DoLineTrace(pawn, linetraceData);
|
|
|
|
Line brokenLine = LineTraceFindbrokenLine(linetraceData);
|
|
|
|
if(brokenLine) {
|
|
|
|
if(loader.originalLinedefSpecials2.CheckKey(brokenLine.index())) {
|
|
|
|
// pawn.A_Log(string.Format("Blep3 %d",
|
|
// loader.originalLinedefSpecials2.Get(hitLine.index())));
|
|
|
|
int originalSpecial = loader.originalLinedefSpecials2.Get(brokenLine.index());
|
|
int currentSpecial = brokenLine.special;
|
|
|
|
if(originalSpecial != currentSpecial) {
|
|
|
|
// Remove battery from inventory.
|
|
int batteryCharges = batteryAmmo.TakeMag(true);
|
|
Console.printf("batteryCharges: %d", batteryCharges);
|
|
|
|
if(batteryCharges < 1) {
|
|
|
|
// Handle getting an empty battery. Just give it back.
|
|
invoker.owner.A_Log("No charged batteries available to use.");
|
|
batteryAmmo.AddAMag(batteryCharges);
|
|
|
|
} else {
|
|
|
|
JumperCablesDeployed deployed = JumperCablesDeployed(
|
|
Spawn(
|
|
"JumperCablesDeployed",
|
|
linetraceData.hitLocation - linetraceData.hitDir * 4));
|
|
|
|
deployed.lineDefIndex = brokenLine.index();
|
|
deployed.bNoGravity = true;
|
|
deployed.charges = batteryCharges;
|
|
|
|
if(linetraceData.hitType == TRACE_HitFloor || linetraceData.hitType == TRACE_HitCeiling) {
|
|
deployed.SetFlatSprite(invoker.owner.angle);
|
|
} else {
|
|
Vector2 lineDir = linetraceData.hitLine.v2.p - linetraceData.hitLine.v1.p;
|
|
deployed.SetWallSprite(lineDir);
|
|
}
|
|
|
|
brokenLine.special = originalSpecial;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
override void DrawHUDStuff(HDStatusBar statusBar, HDWeapon weapon, HDPlayerPawn pawn)
|
|
{
|
|
// Copied and modified from ThunderBuster display.
|
|
if(statusBar.hudlevel == 1) {
|
|
statusBar.DrawBattery(
|
|
-54, -4,
|
|
statusBar.DI_SCREEN_CENTER_BOTTOM,
|
|
reloadorder : true);
|
|
statusBar.DrawNum(
|
|
pawn.CountInv("HDBattery"),
|
|
-46, -8,
|
|
statusBar.DI_SCREEN_CENTER_BOTTOM);
|
|
}
|
|
}
|
|
|
|
override String GetHelpText()
|
|
{
|
|
return WEPHELP_FIRE.." Deploy\n";
|
|
}
|
|
}
|
|
|