Compare commits

...

6 Commits

Author SHA1 Message Date
3f4ceb4afd Removed last TODO. 2023-09-09 16:48:33 -07:00
739474a774 Added a bunch of pride flags. 2023-09-09 16:44:13 -07:00
c830efa728 Pre-stream whatever. 2023-09-09 14:34:54 -07:00
13a99783b3 Cleanup and stuff. 2023-09-09 13:00:20 -07:00
a521cd6074 Custom sprays can now work in theory. 2023-09-09 12:31:23 -07:00
4d5e7f1d0e Initial spraycan implementation. 2023-09-09 09:02:54 -07:00
40 changed files with 726 additions and 2 deletions

13
TODO.md
View File

@ -14,4 +14,17 @@
- ~~Caco plush sprite scaling~~
- ~~Frag counter in loadout starts with no battery~~
- ~~Organize source_data~~
- ~~Spraycan spawn CVars~~
- ~~Spraycan bulk~~
- ~~More spray patterns~~
- ~~NB~~
- ~~Ace~~
- ~~Progress~~
- ~~Demi~~
- ~~Switch~~
- ~~Pan~~
- ~~Bi~~
- ~~Gay (rainbow)~~
- ~~Make firemode switch spray patterns instead of altfire~~
- ~~Loadout code descriptions (hdweapon.loadoutcodes)~~

View File

@ -1 +1,2 @@
server int snektech_spawnflags = 3;
server int snektech_spawnflags = 11;
user string snektech_spraypattern = "SnekSpray_TransPride";

88
decaldef.txt Normal file
View File

@ -0,0 +1,88 @@
fader KiriInstantFade
{
DecayStart 60.0
DecayTime 0.0
}
decal KiriTestDecal
{
pic KSPYA0
animator KiriInstantFade
randomflipx
}
decal SnekSpray_TransPride
{
pic KSPYA0
animator KiriInstantFade
randomflipx
translucent 0.8
}
decal SnekSpray_LesbianPride
{
pic KSPYB0
animator KiriInstantFade
randomflipx
translucent 0.8
}
decal SnekSpray_NBPride
{
pic KSPYC0
animator KiriInstantFade
randomflipx
translucent 0.8
}
decal SnekSpray_AcePride
{
pic KSPYD0
animator KiriInstantFade
randomflipx
translucent 0.8
}
decal SnekSpray_ProgressPride
{
pic KSPYE0
animator KiriInstantFade
translucent 0.8
}
decal SnekSpray_DemiPride
{
pic KSPYF0
animator KiriInstantFade
translucent 0.8
}
decal SnekSpray_PanPride
{
pic KSPYG0
animator KiriInstantFade
translucent 0.8
}
decal SnekSpray_SwitchPride
{
pic KSPYH0
animator KiriInstantFade
}
decal SnekSpray_BiPride
{
pic KSPYI0
animator KiriInstantFade
translucent 0.8
randomflipx
}
decal SnekSpray_GayPride
{
pic KSPYJ0
animator KiriInstantFade
translucent 0.8
randomflipx
}

View File

@ -10,5 +10,6 @@ OptionMenu "SnekTechMenu"
FlagOption "Gretchen Counter", "snektech_spawnflags", "YesNo", 0
FlagOption "Wiring Bypass Kit", "snektech_spawnflags", "YesNo", 1
FlagOption "Cacodemon Plushie", "snektech_spawnflags", "YesNo", 2
FlagOption "Spraypaint Can", "snektech_spawnflags", "YesNo", 3
}

View File

@ -1,5 +1,9 @@
kiri/gretchencounter_click "sounds/gretchencounter_click.ogg"
kiri/gretchencounter_blip "sounds/gretchencounter_blip.ogg"
kiri/gretchencounter_onoff "sounds/gretchencounter_onoff.ogg"
kiri/cacoplushie_throw "sounds/cacoplushie_throw.ogg"
kiri/cacoplushie_despawn "sounds/cacoplushie_despawn.ogg"
kiri/spraycan_rattle "sounds/spraycan_rattle.ogg"
kiri/spraycan_spray "sounds/spraycan_spray.ogg"

BIN
sounds/spraycan_rattle.aup3 Normal file

Binary file not shown.

BIN
sounds/spraycan_rattle.ogg Normal file

Binary file not shown.

BIN
sounds/spraycan_spray.aup3 Normal file

Binary file not shown.

BIN
sounds/spraycan_spray.ogg Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
sprites/spraycan/kspra0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
sprites/spraycan/ksprb0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
sprites/spraycan/kspya0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
sprites/spraycan/kspyb0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
sprites/spraycan/kspyc0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
sprites/spraycan/kspyd0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
sprites/spraycan/kspye0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
sprites/spraycan/kspyf0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
sprites/spraycan/kspyg0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
sprites/spraycan/kspyi0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
sprites/spraycan/kspyj0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -106,3 +106,93 @@ Sprite "KCPLG0", 130, 108
Patch "sprites/cacoplush/cacoplushie_glowing3.png", 0, 0
}
// ----------------------------------------------------------------------
// Spray can
Sprite "KSPRB0", 48, 48
{
Offset 24, 24
Patch KSPRB0,0,0 { }
}
Sprite "KSPYA0", 250, 150
{
XScale 2
YScale 2
Offset 125, 75
Patch KSPYA0,0,0 { }
}
Sprite "KSPYB0", 250, 150
{
XScale 2
YScale 2
Offset 125, 75
Patch KSPYB0,0,0 { }
}
Sprite "KSPYC0", 250, 150
{
XScale 2
YScale 2
Offset 125, 75
Patch KSPYC0,0,0 { }
}
Sprite "KSPYD0", 250, 150
{
XScale 2
YScale 2
Offset 125, 75
Patch KSPYD0,0,0 { }
}
Sprite "KSPYE0", 250, 150
{
XScale 2
YScale 2
Offset 125, 75
Patch KSPYE0,0,0 { }
}
Sprite "KSPYF0", 250, 150
{
XScale 2
YScale 2
Offset 125, 75
Patch KSPYF0,0,0 { }
}
Sprite "KSPYG0", 250, 150
{
XScale 2
YScale 2
Offset 125, 75
Patch KSPYG0,0,0 { }
}
Sprite "KSPYH0", 32, 32
{
XScale 1
YScale 1
Offset 16, 16
Patch SW1S0,0,0 { }
}
Sprite "KSPYI0", 250, 150
{
XScale 2
YScale 2
Offset 125, 75
Patch KSPYI0,0,0 { }
}
Sprite "KSPYJ0", 250, 150
{
XScale 2
YScale 2
Offset 125, 75
Patch KSPYJ0,0,0 { }
}

View File

@ -4,3 +4,4 @@ version "4.10"
#include "zscript/jumpercables.zs"
#include "zscript/gretchencounter.zs"
#include "zscript/cacoplushie.zs"
#include "zscript/spraycan.zs"

View File

@ -9,7 +9,8 @@ enum SnekTechSpawnFlags
{
SNEKTECH_GRETCHENCOUNTER = 0,
SNEKTECH_JUMPERCABLES = 1,
SNEKTECH_CACOPLUSHIE = 2
SNEKTECH_CACOPLUSHIE = 2,
SNEKTECH_SPRAYCAN = 3
}
class SnekTechEventHandler : EventHandler
@ -80,6 +81,11 @@ class SnekTechEventHandler : EventHandler
e.Thing,
SNEKTECH_CACOPLUSHIE,
"KiriCacodemonPlushie");
DoSnekTechBackpackSpawnCheck(
e.Thing,
SNEKTECH_SPRAYCAN,
"Sprayer");
}
override void CheckReplacement(ReplaceEvent e)
@ -106,6 +112,8 @@ class SnekTechEventHandler : EventHandler
SNEKTECH_CACOPLUSHIE,
"BlurSphere", "KiriCacodemonPlushie",
6, e);
// Note: No replacements for spray cans.
}
}

518
zscript/spraycan.zs Normal file
View File

@ -0,0 +1,518 @@
// FIXME: Make consts and enums consistent formatting.
// FIXME: Add bulk.
const KIRI_SPRAY_DISTANCE = 96;
const KIRI_SPRAY_SHAKEANIM_MAXANGLE = 20.0;
const KIRI_SPRAY_MAXPAINT = 50;
const KIRI_SPRAY_MAXPRESSURE = 100;
const KIRI_SPRAY_PRESSUREBUILDSCALE = 20;
const KIRI_SPRAY_PRESSURE_PER_USE = (KIRI_SPRAY_MAXPRESSURE / 2);
const KIRI_SPRAY_PRESSURE_DECAY_RATE = 0.1;
const HDLD_KIRI_SPRAYCAN = "ksp";
enum KiriSprayerStatus
{
KIRI_SPRAY_WS_PAINT = 1,
KIRI_SPRAY_WS_PRESSURE = 2
}
class Sprayer : HDWeapon
{
default
{
radius 2;
height 4;
+SpriteAngle;
Scale 0.4;
+hdweapon.fitsinbackpack;
+weapon.wimpy_weapon;
+INVENTORY.PERSISTENTPOWER;
+INVENTORY.INVBAR;
// inventory.icon "KSPRB0";
inventory.pickupsound "kiri/spraycan_rattle";
inventory.pickupmessage "Picked up some spraypaint cans.";
inventory.amount 1;
inventory.maxamount 100;
hdweapon.refid HDLD_KIRI_SPRAYCAN;
tag "Spraypaint Can";
}
states
{
spawn:
KSPR B -1;
stop;
ready:
KSPR A 1 {
A_WeaponReady(WRF_ALL);
A_WeaponBusy(false);
}
goto readyend;
altfire:
reload:
KSPR A 1 offset(0, 20) {
A_OverlayPivot(0, 0.5, 1.0);
A_OverlayRotate(0, 0);
A_StartSound(
"kiri/spraycan_rattle",
CHAN_WEAPON, CHANF_NOSTOP);
}
KSPR A 1 offset(0, 40) { A_OverlayRotate(0, sin(level.time * 5.0) * KIRI_SPRAY_SHAKEANIM_MAXANGLE * 1.0/3.0); }
KSPR A 1 offset(0, 60) { A_OverlayRotate(0, sin(level.time * 5.0) * KIRI_SPRAY_SHAKEANIM_MAXANGLE * 2.0/3.0); }
KSPR A 1 offset(0, 80) {
A_OverlayRotate(0, sin(level.time * 5.0) * KIRI_SPRAY_SHAKEANIM_MAXANGLE);
// Taper off pressure increase amount based on how much
// pressure is in there already.
float pressureIncreaseScale = 1.0 - (
float(invoker.GetPressure()) /
float(KIRI_SPRAY_MAXPRESSURE));
pressureIncreaseScale *= pressureIncreaseScale;
// Add pressure.
int pressure = invoker.GetPressure();
pressure +=
random(0,
KIRI_SPRAY_PRESSUREBUILDSCALE
* invoker.weaponstatus[KIRI_SPRAY_WS_PAINT]
/ KIRI_SPRAY_MAXPAINT);
// Cap pressure amount.
if(pressure > KIRI_SPRAY_MAXPRESSURE) {
pressure = KIRI_SPRAY_MAXPRESSURE;
}
invoker.SetPressure(pressure);
}
KSPR A 1 offset(0, 60) { A_OverlayRotate(0, sin(level.time * 5.0) * KIRI_SPRAY_SHAKEANIM_MAXANGLE * 2.0/3.0); }
KSPR A 1 offset(0, 40) { A_OverlayRotate(0, sin(level.time * 5.0) * KIRI_SPRAY_SHAKEANIM_MAXANGLE * 1.0/3.0); }
KSPR A 1 offset(0, 20) { A_OverlayRotate(0, 0); }
goto ready;
fire:
KSPR A 2 {
if(invoker.GetPressure() < KIRI_SPRAY_PRESSURE_PER_USE) {
invoker.owner.A_Log("Not enough pressure to spray.");
} else {
Actor spawnedThing;
bool success;
float zOffset = GunHeight() * 0.8;
FLineTraceData lineTraceData;
class<SprayerPattern> sprayerPatternClass = "SprayerPattern";
// Find the spray pattern class that matches the
// player's CVar for selected pattern.
String currentSprayCVar = CVar.GetCVar(
"snektech_spraypattern",
invoker.owner.player).GetString();
for(int i = 0; i < AllActorClasses.size(); i++) {
class<SprayerPattern> thisPatternClass = (class<SprayerPattern>)(AllActorClasses[i]);
if(thisPatternClass) {
if(thisPatternClass.GetClassName() == currentSprayCVar) {
sprayerPatternClass = (class<SprayerPattern>)(AllActorClasses[i]);
break;
}
}
}
// Get the actual decal name out of it.
let defaultSprayClass = GetDefaultByType(sprayerPatternClass);
String actualDecalName = defaultSprayClass.decalName;
// Find a wall to spray on.
bool traceHit = LineTrace(
angle,
KIRI_SPRAY_DISTANCE,
pitch,
flags : TRF_THRUACTORS,
offsetz : zOffset,
data : lineTraceData);
if(traceHit && lineTraceData.HitLine) {
A_StartSound(
"kiri/spraycan_spray",
CHAN_WEAPON);
[success, spawnedThing] = A_SpawnItemEx(
"SprayerDecalSpawner",
xofs : 0, yofs : 0, zofs : zOffset);
SprayerDecalSpawner spawner = SprayerDecalSpawner(spawnedThing);
if(success && spawner) {
spawner.A_SetPitch(pitch);
spawner.A_SetAngle(angle);
spawner.actualDecalName = actualDecalName;
spawner.master = invoker.owner;
invoker.SetPressure(invoker.GetPressure() - KIRI_SPRAY_PRESSURE_PER_USE);
invoker.weaponstatus[KIRI_SPRAY_WS_PAINT] -= 1;
}
} else {
invoker.owner.A_Log("Not close enough to a wall to spray.");
}
}
}
goto waiting;
firemode:
KSPR A 2 { invoker.CycleSprayPattern(); }
goto waiting;
waiting:
KSPR A 5;
KSPR A 0 A_Refire("waiting");
goto ready;
select0:
KSPR A 0 offset(0, 120);
---- A 1 A_Raise(12);
wait;
deselect0:
KSPR A 0;
---- A 1 A_Lower(12);
wait;
}
override bool AddSpareWeapon(actor newowner)
{
return AddSpareWeaponRegular(newowner);
}
override HDWeapon GetSpareWeapon(actor newowner, bool reverse, bool doselect)
{
return GetSpareWeaponRegular(newowner, reverse, doselect);
}
// Why do we store pressure as this weird equation?
//
// Pressure is stored as a time since the a hypothetical last
// "fully charged" time, based on the level time and pressure
// decay rate.
//
// This way we can decay pressure without ticking, important for
// spare weapons and stuff in backpacks.
int GetPressure() const
{
int time = level.TotalTime;
int lastChargedTime = weaponstatus[KIRI_SPRAY_WS_PRESSURE];
int pressure = int(
float(KIRI_SPRAY_MAXPRESSURE) -
float(time - lastChargedTime) * KIRI_SPRAY_PRESSURE_DECAY_RATE);
if(pressure > KIRI_SPRAY_MAXPRESSURE) {
pressure = KIRI_SPRAY_MAXPRESSURE;
}
if(pressure < 0) {
pressure = 0;
}
return pressure;
}
void SetPressure(int pressure)
{
if(pressure > KIRI_SPRAY_MAXPRESSURE) {
pressure = KIRI_SPRAY_MAXPRESSURE;
}
if(pressure < 0) {
pressure = 0;
}
int time = level.TotalTime;
int lastChargedTime = round(
float(pressure - KIRI_SPRAY_MAXPRESSURE) / float(KIRI_SPRAY_PRESSURE_DECAY_RATE) + time);
weaponstatus[KIRI_SPRAY_WS_PRESSURE] = lastChargedTime;
}
void ResetPressure()
{
int resetValue = -int(float(KIRI_SPRAY_MAXPRESSURE) / float(KIRI_SPRAY_PRESSURE_DECAY_RATE));
weaponstatus[KIRI_SPRAY_WS_PRESSURE] = resetValue;
}
override void Consolidate()
{
ResetPressure();
}
override void InitializeWepStats(bool idfa)
{
super.InitializeWepStats(idfa);
SetPressure(0);
// Add a little bit of randomness to the amount of paint in
// the can.
weaponstatus[KIRI_SPRAY_WS_PAINT] =
KIRI_SPRAY_MAXPAINT
- random(0, KIRI_SPRAY_MAXPAINT / 10);
}
override void DrawHUDStuff(
HDStatusBar statusBar, HDWeapon weapon,
HDPlayerPawn pawn)
{
// Current pressure (top bar).
statusBar.DrawWepNum(
GetPressure(),
KIRI_SPRAY_MAXPRESSURE, posy:-10);
// Total paint remaining (bottom bar).
statusBar.DrawWepNum(
weaponstatus[KIRI_SPRAY_WS_PAINT],
KIRI_SPRAY_MAXPAINT);
}
void GetSprayPatternList(Array<String> ret)
{
ret.clear();
for(int i = 0; i < AllActorClasses.Size(); i++) {
class<Actor> c = AllActorClasses[i];
if(c is "SprayerPattern" && c != "SprayerPattern") {
ret.push(c.GetClassName());
}
}
}
void CycleSprayPattern()
{
// Find the current entry in the list of patterns.
String currentPattern = CVar.GetCVar("snektech_spraypattern", owner.player).GetString();
Array<String> patternList;
GetSprayPatternList(patternList);
int currentIndex = patternList.find(currentPattern);
// Make sure we're actually in the list (CVar might not be in
// list, due to mods getting shuffled around and whatever).
if(currentIndex >= patternList.size()) {
currentIndex = -1;
}
// Increment the index.
int newIndex = (currentIndex + 1) % patternList.size();
// Set the new CVar.
CVar.GetCVar("snektech_spraypattern", owner.player).SetString(patternList[newIndex]);
currentPattern = CVar.GetCVar("snektech_spraypattern", owner.player).GetString();
owner.A_Log("Selected spray pattern: "..currentPattern);
// Current pattern is referenced in the help text, so reset
// it.
A_SetHelpText();
}
override double WeaponBulk()
{
return 10;
}
override String GetHelpText()
{
super.GetHelpText();
int messageIndex = level.time;
Array<String> messages = {
"Deface the tyrant's property.",
"Engage in civil disobedience.",
"Create a beautiful work of art.",
"Show the world that you still exist.",
"Defy the tyrant."
};
String currentPattern = CVar.GetCVar("snektech_spraypattern", owner.player).GetString();
return
WEPHELP_FIREMODE .. " Cycle pattern (current: "..currentPattern..").\n"..
WEPHELP_ALTFIRE .. " Shake the can.\n"..
WEPHELP_FIRE .. " "..messages[messageIndex % messages.Size()].."\n";
}
}
class SprayerDecalSpawner : Actor
{
default
{
+nogravity;
}
states
{
spawn:
TNT1 A -1;
stop;
}
int timeSinceLastSpray;
int thisSprayerId;
String actualDecalName;
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_SprayDecal(actualDecalName, KIRI_SPRAY_DISTANCE);
// Figure out a new ID number.
ThinkerIterator iter = ThinkerIterator.Create("SprayerDecalSpawner");
SprayerDecalSpawner otherSpawner;
int maxId = 0;
while(otherSpawner = SprayerDecalSpawner(iter.Next())) {
if(otherSpawner == self) {
continue;
}
// Keep track of the highest ID.
if(otherSpawner.thisSprayerId > maxId) {
if(otherSpawner.master == master) {
maxId = otherSpawner.thisSprayerId;
}
}
}
thisSprayerId = maxId + 1;
// Clear old sprayers.
iter = ThinkerIterator.Create("SprayerDecalSpawner");
while(otherSpawner = SprayerDecalSpawner(iter.Next())) {
if(otherSpawner == self) {
continue;
}
if(otherSpawner.thisSprayerId < (thisSprayerId - 5)) {
if(otherSpawner.master == master) {
otherSpawner.Destroy();
}
}
}
}
override void Tick()
{
super.Tick();
timeSinceLastSpray++;
if(timeSinceLastSpray >= 35 * 60) {
A_SprayDecal(actualDecalName, KIRI_SPRAY_DISTANCE);
timeSinceLastSpray = 0;
}
}
}
class SprayerPattern : Actor
{
string decalName;
property decalName:decalName;
default {
SprayerPattern.decalName "KiriTestDecal";
}
}
class SnekSpray_TransPride : SprayerPattern
{
default
{
SprayerPattern.decalName "SnekSpray_TransPride";
}
}
class SnekSpray_LesbianPride : SprayerPattern
{
default
{
SprayerPattern.decalName "SnekSpray_LesbianPride";
}
}
class SnekSpray_NBPride : SprayerPattern
{
default
{
SprayerPattern.decalName "SnekSpray_NBPride";
}
}
class SnekSpray_AcePride : SprayerPattern
{
default
{
SprayerPattern.decalName "SnekSpray_AcePride";
}
}
class SnekSpray_ProgressPride : SprayerPattern
{
default
{
SprayerPattern.decalName "SnekSpray_ProgressPride";
}
}
class SnekSpray_DemiPride : SprayerPattern
{
default
{
SprayerPattern.decalName "SnekSpray_DemiPride";
}
}
class SnekSpray_PanPride : SprayerPattern
{
default
{
SprayerPattern.decalName "SnekSpray_PanPride";
}
}
class SnekSpray_SwitchPride : SprayerPattern
{
default
{
SprayerPattern.decalName "SnekSpray_SwitchPride";
}
}
class SnekSpray_BiPride : SprayerPattern
{
default
{
SprayerPattern.decalName "SnekSpray_BiPride";
}
}
class SnekSpray_GayPride : SprayerPattern
{
default
{
SprayerPattern.decalName "SnekSpray_GayPride";
}
}