More adding stuff.

This commit is contained in:
Kiri 2023-09-02 20:48:24 -07:00
parent 2599b739d8
commit 43d292332a
64 changed files with 797 additions and 67 deletions

19
CREDITS Normal file
View File

@ -0,0 +1,19 @@
Sprites by Kiri.
(Deployed battery sprite modified from id Software original sprite.)
Code by Kiri.
Jokes by Twitch chat (thanks, Drikanis), so blame them.
prettyFist for general advisement.
People keeping me company in VC right now:
- Clance
- prettyFist
- Generic Firedemon
- Jenin
- Mia-chan
- Squarepog
- Fay

View File

@ -1 +1 @@
server int snektech_spawnflags = 1; server int snektech_spawnflags = 3;

View File

@ -1,3 +1,6 @@
// ----------------------------------------------------------------------
// Gretchen counter brightmaps
brightmap sprite kgcma0 brightmap sprite kgcma0
{ {
map "sprites/gretchencounter/bright_kgcma0.png" map "sprites/gretchencounter/bright_kgcma0.png"
@ -33,3 +36,27 @@ brightmap sprite kgcpb0
map "sprites/gretchencounter/bright_kgcpb0.png" map "sprites/gretchencounter/bright_kgcpb0.png"
disablefullbright disablefullbright
} }
// ----------------------------------------------------------------------
// Jumper cables brightmaps
brightmap sprite jmppa0
{
map "sprites/bright_jmpr_1.png"
disablefullbright
}
brightmap sprite jmppb0
{
map "sprites/bright_jmpr_2.png"
disablefullbright
}
brightmap sprite jmppc0
{
map "sprites/bright_jmpr_3.png"
disablefullbright
}

View File

@ -1,5 +1,6 @@
gameinfo gameinfo
{ {
AddEventHandlers = "SnekTechEventHandler" AddEventHandlers = "SnekTechEventHandler"
AddEventHandlers = "JumperCablesMapLoader"
} }

View File

@ -8,5 +8,6 @@ OptionMenu "SnekTechMenu"
Title "SnekTech Discount Party Pack" Title "SnekTech Discount Party Pack"
StaticText "----- Item Spawns -----", "Teal" StaticText "----- Item Spawns -----", "Teal"
FlagOption "Gretchen Counter", "snektech_spawnflags", "YesNo", 0 FlagOption "Gretchen Counter", "snektech_spawnflags", "YesNo", 0
FlagOption "Wiring Bypass Kit", "snektech_spawnflags", "YesNo", 1
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,3 +1,6 @@
// ----------------------------------------------------------------------
// Gretchen Counter
sprite KGCPA0, 32, 32 { sprite KGCPA0, 32, 32 {
offset 16, 32 offset 16, 32
patch KGCPA0,0,0 { } patch KGCPA0,0,0 { }
@ -7,3 +10,53 @@ sprite KGCPB0, 32, 32 {
offset 16, 32 offset 16, 32
patch KGCPB0,0,0 { } patch KGCPB0,0,0 { }
} }
// ----------------------------------------------------------------------
// Jumper cables
sprite JMPRA0, 49, 43 {
offset 24, 21
patch JMPRA0,0,0 { }
}
sprite JMPRB0, 49, 43 {
offset 24, 21
patch JMPRA0,0,0 { translation "112:120=160:167" }
}
sprite JMPRC0, 49, 43 {
offset 24, 21
patch JMPRA0,0,0 { translation "112:120=32:47" }
}
sprite JMPRD0, 49, 43 {
offset 24, 21
patch JMPRA0,0,0 { translation "112:120=96:111" }
}
sprite JMPKA0, 48, 32 {
offset 24, 16
patch JMPKA0,0,0 { }
}
// Weapon sprites.
//
// Note that there's a corresponding offset in the ledd_left_indicator
// and ledd_right_indicator states in object itself, which offset the
// text into position.
sprite JMPPA0, 320, 200 {
offset 0, 32
patch JMPPA0,0,0 { }
}
sprite JMPPB0, 320, 200 {
offset 0, 32
patch JMPPB0,0,0 { }
}
sprite JMPPC0, 320, 200 {
offset 0, 32
patch JMPPC0,0,0 { }
}

View File

@ -1,4 +1,5 @@
version "4.10" version "4.10"
#include "zscript/snektech.zs" #include "zscript/snektech.zs"
#include "zscript/jumpercables.zs"
#include "zscript/gretchencounter.zs" #include "zscript/gretchencounter.zs"

View File

@ -160,7 +160,6 @@ class GretchenCounter : HDWeapon
KGCM B 1 { KGCM B 1 {
A_Overlay(355, "needle_indicator"); A_Overlay(355, "needle_indicator");
A_OverlayPivotAlign(355, PSPA_CENTER, PSPA_BOTTOM); A_OverlayPivotAlign(355, PSPA_CENTER, PSPA_BOTTOM);
// invoker.UpdateDisplay();
updateOverlay(); updateOverlay();
// USER3 = MagManager. // USER3 = MagManager.
@ -178,7 +177,7 @@ class GretchenCounter : HDWeapon
KGCM # 1 { KGCM # 1 {
invoker.weaponstatus[KGC_ACTIVE] = invoker.weaponstatus[KGC_ACTIVE] =
!invoker.weaponstatus[KGC_ACTIVE]; !invoker.weaponstatus[KGC_ACTIVE];
A_PlaySound("kiri/gretchencounter_onoff"); A_StartSound("kiri/gretchencounter_onoff");
updateOverlay(); updateOverlay();
} }
@ -295,7 +294,6 @@ class GretchenCounter : HDWeapon
statusBar.DI_SCREEN_CENTER_BOTTOM, statusBar.DI_SCREEN_CENTER_BOTTOM,
Font.CR_DARKGRAY); Font.CR_DARKGRAY);
} }
} }
bool GetIsActive() bool GetIsActive()
@ -331,28 +329,7 @@ class GretchenCounter : HDWeapon
referenceActor = self; referenceActor = self;
} }
// ThinkerIterator iter = ThinkerIterator.Create("BFGNecroShard"); // Get the actual reading.
// Actor mo;
// int shardCount = 0;
// float totalReading = 0.0;
// while(mo = BFGNecroShard(iter.Next()))
// {
// //shardCount++;
// float dist = Distance3D(mo);
// // Convert to "meters" (with a minimum).
// if(dist < 32) {
// dist = 32;
// }
// dist /= 32.0;
// // // Arbitrary scaling.
// // dist /= 16.0;
// totalReading += 1.0 / (dist * dist);
// shardCount += 1;
// }
float totalReading = 0.0; float totalReading = 0.0;
if(GetIsActive()) { if(GetIsActive()) {
totalReading = totalReading =
@ -360,63 +337,37 @@ class GretchenCounter : HDWeapon
GetReadingForType("HDBarrel", 2.0); GetReadingForType("HDBarrel", 2.0);
} }
// lastReading = totalReading * 0.01 + lastReading * 0.99;
float pct_reading = lastReading * 2000.0; float pct_reading = lastReading * 2000.0;
lastReading = totalReading; lastReading = totalReading;
// Make clicky noises if we're getting readings.
float r = frandom(0.0, 1.0); float r = frandom(0.0, 1.0);
if(r < pct_reading && GetIsActive()) if(r < pct_reading && GetIsActive())
{ {
// console.printf("test: %f %f", r, pct_reading);
referenceActor.A_StartSound("kiri/gretchencounter_click"); referenceActor.A_StartSound("kiri/gretchencounter_click");
} }
// Apply some physics to the needle rotation.
// if(pct_reading > 1.0) {
// pct_reading = 1.0;
// }
// String meter_str = "";
// float k;
// for(k = 0.0; k < 1.0; k += 0.02) {
// if(k < pct_reading) {
// meter_str = meter_str.."#";
// } else {
// meter_str = meter_str.."_";
// }
// }
// if(pct_reading > 1.0) {
// pct_reading = 1.0;
// }
// A_OverlayRotate(355,
// angle_min + (angle_max - angle_min) * (1.0 - pct_reading));
angularvelocity += pct_reading - needlePosition; angularvelocity += pct_reading - needlePosition;
angularvelocity *= 0.97; angularvelocity *= 0.97;
// Check to see if the needle was at max last frame. If it
// wasn't at max before, and it is now, we need to start
// beeping.
bool wasNeedleAtMax = (needlePosition >= 0.9); bool wasNeedleAtMax = (needlePosition >= 0.9);
needlePosition += angularvelocity * 0.1; needlePosition += angularvelocity * 0.1;
needlePosition = clamp(needlePosition, 0.0, 1.0); needlePosition = clamp(needlePosition, 0.0, 1.0);
// Play a "beep" if the needle is at max, and it's either been
// console.printf("Ang vel %f", angularvelocity); // long enough or we weren't at max before. Make it start and
// console.printf("Shard count: [%s] %f", meter_str, pct_reading); // stop.
framesSinceLastBeep++; framesSinceLastBeep++;
// if(GetIsActive()) if(!wasNeedleAtMax || framesSinceLastBeep > 15) {
{ if(needlePosition >= 0.9) {
if(!wasNeedleAtMax || framesSinceLastBeep > 15) { referenceActor.A_StartSound("kiri/gretchencounter_blip");
if(needlePosition >= 0.9) { framesSinceLastBeep = 0;
referenceActor.A_StartSound("kiri/gretchencounter_blip");
framesSinceLastBeep = 0;
}
} }
} }
} }

660
zscript/jumpercables.zs Normal file
View File

@ -0,0 +1,660 @@
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;
bool worldScanCompleted;
static JumperCablesMapLoader Get()
{
JumperCablesMapLoader loader;
loader = JumperCablesMapLoader(EventHandler.Find("JumperCablesMapLoader"));
return loader;
}
override void WorldUnloaded(WorldEvent e)
{
JumperCablesMapLoader loader = Get();
// Clear out anything from the last level.
deployedCables.Clear();
loader.originalLinedefSpecials2.Clear();
worldScanCompleted = false;
}
override void WorldTick()
{
JumperCablesMapLoader loader = Get();
if(!loader.worldScanCompleted) {
// 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);
}
}
loader.worldScanCompleted = true;
}
}
override void WorldLineActivated(WorldEvent e)
{
// Is this line affected by a battery?
for(int cableIndex = 0; cableIndex < deployedCables.size(); cableIndex++) {
if(deployedCables[cableIndex].lineDefIndex == e.ActivatedLine.index()) {
// Yes. Deplete the battery.
deployedCables[cableIndex].DecrementBattery();
break;
}
}
}
void RegisterCables(JumperCablesDeployed cableActor)
{
if(deployedCables.Find(cableActor) == deployedCables.size()) {
deployedCables.Push(cableActor);
}
}
void UnregisterCables(JumperCablesDeployed cableActor)
{
int index = deployedCables.find(cableActor);
if(index != deployedCables.size()) {
deployedCables.delete(index);
}
}
}
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()
{
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();
}
// Despawn to battery if the sector gets re-destroyed.
if(level.lines[lineDefIndex].special == 0)
{
DespawnToBattery();
}
}
}
class JumperCablesUsable : HDWeapon
{
int lastIndicatorStatus;
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);
}
override void DrawSightPicture(
HDStatusBar statusBar,
HDWeapon weapon,
HDPlayerPawn playerPawn,
bool sightbob,
Vector2 bob,
double fov,
bool scopeview,
Actor hpc,
String whichdot)
{
// Switch reticle colors depending on the LED status.
String filename = String.Format(
"graphics/jumpercables/jmpr_reticle%d.png",
lastIndicatorStatus + 1);
statusBar.drawImage(
filename,
(0, 0) + bob,
statusBar.DI_SCREEN_CENTER | statusBar.DI_ITEM_TOP,
scale : (0.5, 0.5));
}
states
{
spawn:
JMPP # 0 offset(0, -32);
JMPK A -1;
stop;
select0:
JMPP A 1;
goto select0big;
deselect0:
JMPP A 1;
goto deselect0big;
// LCD overlay states.
ledd_left_indicator:
JMIL A 1 offset(0, -32); wait;
ledd_right_indicator:
JMIR A 1 offset(0, -32); wait;
ready:
JMPP # 1 {
if(PressingFire()) {
SetWeaponState("deploy");
}
A_WeaponReady(WRF_NOFIRE | WRF_ALLOWUSER3); // USER3 = MagManager.
// Line trace to see what we might be inspecting.
FLineTraceData linetraceData;
DoLineTrace(HDPlayerPawn(invoker.owner), linetraceData);
// Overlay setup.
A_Overlay(355, "ledd_left_indicator");
A_Overlay(356, "ledd_right_indicator");
// Update displays.
UpdateVisualsForLineTrace(
HDPlayerPawn(invoker.owner),
linetraceData);
}
goto readyend;
readyend:
#### # 0 A_ReadyEnd();
#### # 0 A_Jump(256,"ready");
deploy:
// Lower it down...
JMPP A 1 offset(0, 16);
JMPP A 1 offset(0, 32);
JMPP A 1 offset(0, 48);
JMPP A 1 offset(0, 64);
JMPP A 1 offset(0, 72);
JMPP A 1 offset(0, 96);
JMPP A 1 offset(0, 128);
// Play some sounds.
JMPP A 0 A_JumpIf(!pressingfire(),"ready");
JMPP A 4 A_StartSound("weapons/pismagclick",CHAN_WEAPON);
JMPP A 2 A_StartSound("derp/crawl",CHAN_WEAPON,CHANF_OVERLAP);
// Actual cable attachment happens here.
JMPP A 1 {
AttachCables(HDPlayerPawn(invoker.owner));
}
// Raise it back up...
JMPP A 1 offset(0, 128);
JMPP A 1 offset(0, 96);
JMPP A 1 offset(0, 72);
JMPP A 1 offset(0, 64);
JMPP A 1 offset(0, 48);
JMPP A 1 offset(0, 32);
JMPP A 1 offset(0, 16);
JMPP A 1 offset(0, 0);
JMPP A 1 {
A_WeaponReady(WRF_NOFIRE | WRF_ALLOWRELOAD | WRF_ALLOWUSER4);
}
goto ready;
user3:
#### # 0 A_MagManager("HDBattery");
goto ready;
}
action void UpdateVisualsForLineTrace(HDPlayerPawn pawn, FLineTraceData linetraceData)
{
Line hitLine = linetraceData.hitLine;
String particleColor = "darkred";
Line brokenLine = LineTraceFindbrokenLine(linetraceData);
// Set main frame (LEDs) based on raytrace status.
if(brokenLine) {
// Found somethine we can fix!
particleColor = "green";
player.getpsprite(PSP_WEAPON).frame = 1;
} else {
if(linetraceData.hitType == TRACE_HitNone) {
// Didn't hit anything (yellow light).
player.getpsprite(PSP_WEAPON).frame = 0;
} else{
// Hit something, but it's not broken (green light).
player.getpsprite(PSP_WEAPON).frame = 2;
}
}
// Set display overlay frames.
// Default to random numbers.
int frame_left = random(0, 15);
int frame_right = random(0, 15);
Line lineForDisplay = brokenLine;
if(!lineForDisplay) {
lineForDisplay = hitLine;
}
if(lineForDisplay) {
JumperCablesMapLoader loader = JumperCablesMapLoader.Get();
int originalSpecial = loader.originalLinedefSpecials2.Get(lineForDisplay.index());
frame_right = originalSpecial % 16;
frame_left = originalSpecial / 16;
}
player.getpsprite(355).frame = frame_left;
player.getpsprite(356).frame = frame_right;
// Play a sound if we just discovered a broken line.
if(invoker.lastIndicatorStatus != player.getpsprite(PSP_WEAPON).frame) {
if(invoker.lastIndicatorStatus != 1 && player.getpsprite(PSP_WEAPON).frame == 1) {
A_StartSound("herp/beep", CHAN_WEAPON);
}
}
// Set indicator status fo the HUD.
invoker.lastIndicatorStatus = player.getpsprite(PSP_WEAPON).frame;
// Debug particle display for the linetrace.
if(hd_debug > 1) {
if(linetraceData.hitType != TRACE_HitNone) {
Vector3 hitPos = linetraceData.hitLocation - Vec3Offset(0, 0, 0);
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 | TRF_ABSOFFSET,
offsetz : pawn.gunPos.z - 1,
offsetforward : pawn.gunPos.x,
offsetside : pawn.gunPos.y,
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 (it's working fine, presumably).
if(secLine.special == 0) {
// If this *used* to be special, we DO care about it!
if(loader.originalLinedefSpecials2.CheckKey(secLine.index())) {
// Make sure it's actually changed.
int originalSpecial = loader.originalLinedefSpecials2.Get(secLine.index());
int currentSpecial = secLine.special;
if(originalSpecial != currentSpecial) {
// See if this 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) {
return closestLineInRange;
}
}
return null;
}
action void AttachCables(HDPlayerPawn pawn)
{
// Do we have enough ammo?
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);
// Are we even close enough to a line?
Line brokenLine = LineTraceFindbrokenLine(linetraceData);
if(!brokenLine) {
invoker.owner.A_Log("Not close enough to repair.");
return;
}
if(!loader.originalLinedefSpecials2.CheckKey(brokenLine.index())) {
invoker.owner.A_Log("No mechanism present to repair.");
return;
}
// Check to see if it's something that actually needs fixing.
int originalSpecial = loader.originalLinedefSpecials2.Get(brokenLine.index());
int currentSpecial = brokenLine.special;
if(originalSpecial == currentSpecial) {
invoker.owner.A_Log("This is already functioning as normal.");
return;
}
// Remove battery from inventory, but make sure we're actually
// getting a charged one.
int batteryCharges = batteryAmmo.TakeMag(true);
if(batteryCharges < 1) {
invoker.owner.A_Log("No charged batteries available to use.");
batteryAmmo.AddAMag(batteryCharges); // Just give it back.
return;
}
// Actually spawn the deployed item.
JumperCablesDeployed deployed = JumperCablesDeployed(
Spawn(
"JumperCablesDeployed",
linetraceData.hitLocation - linetraceData.hitDir * 4));
deployed.lineDefIndex = brokenLine.index();
deployed.bNoGravity = true;
deployed.charges = batteryCharges;
// Use a different kind of flat sprite and angle depending on
// if we're attaching to wall or floor/ceiling.
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);
}
// Restore the damn line special already.
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";
}
}

View File

@ -1,6 +1,7 @@
enum SnekTechSpawnFlags enum SnekTechSpawnFlags
{ {
SNEKTECH_GRETCHENCOUNTER = 0 SNEKTECH_GRETCHENCOUNTER = 0,
SNEKTECH_JUMPERCABLES = 1
} }
class SnekTechEventHandler : EventHandler class SnekTechEventHandler : EventHandler
@ -56,7 +57,15 @@ class SnekTechEventHandler : EventHandler
} }
// Delete disabled items from backpacks that just spawned. // Delete disabled items from backpacks that just spawned.
DoSnekTechBackpackSpawnCheck(e.Thing, SNEKTECH_GRETCHENCOUNTER, "GretchenCounter"); DoSnekTechBackpackSpawnCheck(
e.Thing,
SNEKTECH_GRETCHENCOUNTER,
"GretchenCounter");
DoSnekTechBackpackSpawnCheck(
e.Thing,
SNEKTECH_JUMPERCABLES,
"JumperCablesUsable");
} }
override void CheckReplacement(ReplaceEvent e) override void CheckReplacement(ReplaceEvent e)
@ -69,7 +78,15 @@ class SnekTechEventHandler : EventHandler
return; return;
} }
DoSnekTechReplacement(SNEKTECH_GRETCHENCOUNTER, "Stimpack", "GretchenCounter", 12, e); DoSnekTechReplacement(
SNEKTECH_GRETCHENCOUNTER,
"Stimpack", "GretchenCounter",
12, e);
DoSnekTechReplacement(
SNEKTECH_JUMPERCABLES,
"Allmap", "JumperCablesUsable",
256, e);
} }
} }