Split item code.
This commit is contained in:
parent
3ba2e921bc
commit
1fe50b5c32
8 changed files with 4511 additions and 4501 deletions
|
|
@ -1,3 +1,3 @@
|
|||
[default]
|
||||
SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r692 \cu(Thu 15 Dec 14:07:15 CET 2022)\c-";
|
||||
SWWM_SHORTVER="\cw1.3pre r692 \cu(2022-12-15 14:07:15)\c-";
|
||||
SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r693 \cu(Thu 15 Dec 14:31:43 CET 2022)\c-";
|
||||
SWWM_SHORTVER="\cw1.3pre r693 \cu(2022-12-15 14:31:43)\c-";
|
||||
|
|
|
|||
|
|
@ -95,7 +95,10 @@ version "4.10"
|
|||
#include "zscript/items/swwm_health.zsc"
|
||||
#include "zscript/items/swwm_armor.zsc"
|
||||
#include "zscript/items/swwm_armor_filtered.zsc" // Hexen fuckery
|
||||
#include "zscript/items/swwm_miscitems.zsc"
|
||||
#include "zscript/items/swwm_lamp.zsc"
|
||||
#include "zscript/items/swwm_powerups.zsc"
|
||||
#include "zscript/items/swwm_powerups_vip.zsc"
|
||||
#include "zscript/items/swwm_ammoitems.zsc"
|
||||
#include "zscript/items/swwm_ammoextra.zsc"
|
||||
#include "zscript/items/swwm_ammospawn.zsc"
|
||||
|
|
@ -129,6 +132,7 @@ version "4.10"
|
|||
#include "zscript/weapons/swwm_tastytreat_fx.zsc"
|
||||
#include "zscript/weapons/swwm_deathlydeathcannon.zsc"
|
||||
#include "zscript/weapons/swwm_deathlydeathcannon_fx.zsc"
|
||||
#include "zscript/weapons/swwm_deathlydeathcannon_altfx.zsc" // split due to size
|
||||
#include "zscript/weapons/swwm_weapons_hud.zsc"
|
||||
#include "zscript/weapons/swwm_weapons_canvas.zsc"
|
||||
// monsters
|
||||
|
|
|
|||
682
zscript/items/swwm_lamp.zsc
Normal file
682
zscript/items/swwm_lamp.zsc
Normal file
|
|
@ -0,0 +1,682 @@
|
|||
// Lämp
|
||||
|
||||
Class LampMoth : Actor
|
||||
{
|
||||
Actor lamp;
|
||||
Vector3 trail, ofs;
|
||||
int lifespan;
|
||||
|
||||
Default
|
||||
{
|
||||
Tag "$T_MOTH";
|
||||
Radius 2;
|
||||
Height 4;
|
||||
Speed 2;
|
||||
DamageFunction 1;
|
||||
MeleeRange 16;
|
||||
Mass 10;
|
||||
Health 50;
|
||||
DeathSound "moth/die";
|
||||
BloodColor "20 10 10";
|
||||
MONSTER;
|
||||
-COUNTKILL;
|
||||
+THRUACTORS;
|
||||
+NOGRAVITY;
|
||||
+NOTELEPORT;
|
||||
+FLOAT;
|
||||
+NOPAIN;
|
||||
+FRIENDLY;
|
||||
+LOOKALLAROUND;
|
||||
+QUICKTORETALIATE;
|
||||
+INTERPOLATEANGLES;
|
||||
+NOBLOCKMONST;
|
||||
}
|
||||
override string GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
|
||||
{
|
||||
if ( inflictor && (inflictor != self) )
|
||||
{
|
||||
if ( inflictor == master ) return StringTable.Localize("$O_MOTHSELF"); // not likely to happen
|
||||
else return StringTable.Localize("$O_MOTH");
|
||||
}
|
||||
return StringTable.Localize("$O_MOTH2");
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
A_StartSound("moth/fly",CHAN_BODY,CHANF_LOOP,.02,4.,FRandom[Moth](.8,1.2));
|
||||
if ( master && master.player ) SetFriendPlayer(master.player);
|
||||
else bFRIENDLY = false;
|
||||
}
|
||||
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
|
||||
{
|
||||
// no hurt moff
|
||||
if ( source && IsFriend(source) ) damage = 0;
|
||||
return Super.DamageMobj(inflictor,source,damage,mod,flags,angle);
|
||||
}
|
||||
bool isEntranced()
|
||||
{
|
||||
if ( !lamp )
|
||||
{
|
||||
// look for nearby lamps
|
||||
let bi = BlockThingsIterator.Create(self,250);
|
||||
double mindist = 62500.;
|
||||
while ( bi.Next() )
|
||||
{
|
||||
if ( !bi.Thing || !(bi.Thing is 'CompanionLamp') ) continue;
|
||||
Actor a = bi.Thing;
|
||||
double dist = Distance3DSquared(a);
|
||||
if ( (a.frame == 0) || (dist > mindist) && !CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
|
||||
mindist = dist;
|
||||
lamp = a;
|
||||
master = a.target;
|
||||
if ( CompanionLamp(lamp).moff.Find(self) == CompanionLamp(lamp).moff.Size() )
|
||||
CompanionLamp(lamp).moff.Push(self);
|
||||
if ( master && master.player ) SetFriendPlayer(master.player);
|
||||
else bFRIENDLY = false;
|
||||
}
|
||||
}
|
||||
if ( !lamp || (lamp.frame == 0) || (Distance3DSquared(lamp) > 62500) || !CheckSight(lamp,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) return false;
|
||||
if ( target && (target.Health > 0) && CheckSight(target) ) return false;
|
||||
return true;
|
||||
}
|
||||
void A_SmoothWander()
|
||||
{
|
||||
if ( level.Vec3Diff(pos,trail).length() < speed )
|
||||
{
|
||||
double ang = FRandom[Moth](0,360);
|
||||
double pt = FRandom[Moth](-30,30);
|
||||
double dist = FRandom[Moth](20,40);
|
||||
ofs = SWWMUtility.Vec3FromAngles(ang,pt)*dist;
|
||||
}
|
||||
Vector3 newpos = level.Vec3Offset(pos,ofs);
|
||||
if ( level.IsPointInLevel(newpos) ) trail = newpos;
|
||||
if ( vel.length() > 0 )
|
||||
{
|
||||
Vector3 uvel = vel.unit();
|
||||
angle += Clamp(deltaangle(angle,atan2(uvel.y,uvel.x)),-5.,5.);
|
||||
pitch += Clamp(deltaangle(pitch,asin(-uvel.z)),-5.,5.);
|
||||
}
|
||||
vel *= .8;
|
||||
Vector3 dir = level.Vec3Diff(pos,trail);
|
||||
if ( dir.length() > 0 ) vel += dir.unit()*clamp(dir.length()*.05,.4*speed,.5*speed);
|
||||
}
|
||||
void A_SmoothChase()
|
||||
{
|
||||
if ( !target || (target.Health <= 0) )
|
||||
{
|
||||
A_ClearTarget();
|
||||
SetStateLabel("Spawn");
|
||||
return;
|
||||
}
|
||||
if ( CheckMeleeRange() )
|
||||
{
|
||||
SetStateLabel("Melee");
|
||||
return;
|
||||
}
|
||||
Vector3 dest = target.Vec3Offset(0,0,target.height*.75);
|
||||
Vector3 dir = level.Vec3Diff(pos,dest);
|
||||
if ( dir.length() > 0 )
|
||||
{
|
||||
Vector3 dirunit = dir.unit();
|
||||
FLineTraceData d;
|
||||
LineTrace(atan2(dirunit.y,dirunit.x),dir.length(),asin(-dirunit.z),data:d);
|
||||
if ( (d.HitType != TRACE_HitActor) && (d.HitActor != target) )
|
||||
{
|
||||
A_Chase();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( vel.length() > 0 )
|
||||
{
|
||||
Vector3 uvel = vel.unit();
|
||||
angle = atan2(uvel.y,uvel.x);
|
||||
pitch = asin(-uvel.z);
|
||||
}
|
||||
vel *= .8;
|
||||
if ( dir.length() > 0 ) vel += dir.unit()*clamp(dir.length()*.02,.3*speed,2.*speed);
|
||||
}
|
||||
void A_FollowLamp()
|
||||
{
|
||||
if ( !lamp )
|
||||
{
|
||||
SetStateLabel("Spawn");
|
||||
return;
|
||||
}
|
||||
double dst = level.Vec3Diff(pos,trail).length();
|
||||
if ( (dst < speed) || (dst > 50) )
|
||||
{
|
||||
double ang = FRandom[Moth](0,360);
|
||||
double pt = FRandom[Moth](-30,30);
|
||||
double dist = FRandom[Moth](20,30);
|
||||
ofs = SWWMUtility.Vec3FromAngles(ang,pt)*dist;
|
||||
}
|
||||
Vector3 newpos = level.Vec3Offset(lamp.Vec3Offset(0,0,lamp.height/2),ofs);
|
||||
if ( level.IsPointInLevel(newpos) ) trail = newpos;
|
||||
if ( vel.length() > 0 )
|
||||
{
|
||||
Vector3 uvel = vel.unit();
|
||||
angle = atan2(uvel.y,uvel.x);
|
||||
pitch = asin(-uvel.z);
|
||||
}
|
||||
vel *= .8;
|
||||
Vector3 dir = level.Vec3Diff(pos,trail);
|
||||
if ( dir.length() > 0 ) vel += dir.unit()*clamp(dir.length()*.02,.4*speed,2.*speed);
|
||||
Vector3 diff = level.Vec3Diff(pos,lamp.pos);
|
||||
if ( (diff.x > -8) && (diff.x < 8) && (diff.y > -8) && (diff.y < 8) && (diff.z > -4) && (diff.z < lamp.height+4) )
|
||||
{
|
||||
if ( diff.x < 0 ) vel.x -= .2;
|
||||
else vel.x += .2;
|
||||
if ( diff.y < 0 ) vel.y -= .2;
|
||||
else vel.y += .2;
|
||||
if ( diff.z < 0 ) vel.z -= .2;
|
||||
else vel.z += .2;
|
||||
}
|
||||
}
|
||||
void A_SmoothMove()
|
||||
{
|
||||
if ( vel.length() > 0 )
|
||||
{
|
||||
Vector3 uvel = vel.unit();
|
||||
angle = atan2(uvel.y,uvel.x);
|
||||
pitch = asin(-uvel.z);
|
||||
}
|
||||
vel *= .8;
|
||||
}
|
||||
void A_Scrape()
|
||||
{
|
||||
if ( CheckMeleeRange() )
|
||||
{
|
||||
A_FaceTarget(0,0);
|
||||
lifespan -= 5;
|
||||
Vector3 awaydir = level.Vec3Diff(target.Vec3Offset(0,0,target.height),pos).unit();
|
||||
vel += awaydir*8.;
|
||||
int dmg = target.DamageMobj(self,master?master:Actor(self),GetMissileDamage(0,0),'Melee',Random[Moth](0,8)?DMG_NO_PAIN:0);
|
||||
if ( (dmg > 0) && target && !target.bNOBLOOD && !target.bDORMANT && !target.bINVULNERABLE )
|
||||
{
|
||||
target.TraceBleed(dmg,self);
|
||||
target.SpawnBlood(pos,atan2(awaydir.y,awaydir.x)+180,dmg);
|
||||
}
|
||||
A_StartSound("moth/scrape",CHAN_WEAPON,CHANF_OVERLAP,.2,2.5);
|
||||
DamageMobj(target,target,1,'Melee');
|
||||
}
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
Super.Tick();
|
||||
if ( isFrozen() || (freezetics > 0) ) return;
|
||||
if ( isEntranced() )
|
||||
{
|
||||
lifespan = 100;
|
||||
return;
|
||||
}
|
||||
if ( target && (target.Health > 0) ) lifespan = max(20,lifespan);
|
||||
lifespan--;
|
||||
if ( lifespan <= 0 )
|
||||
{
|
||||
let s = Spawn("SWWMSmallSmoke",pos);
|
||||
s.alpha *= .3;
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XZW1 B 0 A_JumpIf(isEntranced(),"See.Entranced");
|
||||
XZW1 BC 1
|
||||
{
|
||||
A_SmoothWander();
|
||||
A_Look();
|
||||
}
|
||||
Loop;
|
||||
See: // go for enemies
|
||||
XZW1 B 0 A_JumpIf(isEntranced(),"See.Entranced");
|
||||
XZW1 BC 1 A_SmoothChase();
|
||||
Loop;
|
||||
See.Entranced: // follow the lamp
|
||||
XZW1 B 0 A_JumpIf(!isEntranced(),"Spawn");
|
||||
XZW1 BC 1 A_FollowLamp();
|
||||
Loop;
|
||||
Melee:
|
||||
XZW1 B 0 A_Scrape();
|
||||
XZW1 BCBC 1 A_SmoothMove();
|
||||
Goto See;
|
||||
Death:
|
||||
TNT1 A 1
|
||||
{
|
||||
A_StartSound("moth/die",CHAN_VOICE,CHANF_OVERLAP,.6,2.5);
|
||||
let s = Spawn("SWWMSmallSmoke",pos);
|
||||
s.alpha *= .3;
|
||||
}
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
Class LampMoth2 : LampMoth
|
||||
{
|
||||
Default
|
||||
{
|
||||
Tag "$T_WMOTH";
|
||||
DamageFunction 3;
|
||||
Speed 3;
|
||||
Scale 1.5;
|
||||
Health 200;
|
||||
}
|
||||
}
|
||||
|
||||
Class LampMashiro : Actor abstract
|
||||
{
|
||||
//
|
||||
// ~nothing here yet, but she will make an appearance someday~
|
||||
//
|
||||
// ⠀⠀⠀⠀⠤⠀⠄⠀⠀⠀⠳⠀⠂⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡣⣁⢌⡀⢄⠁⡁⠝⢿⣿⣿⣿⡻⣿⣿⣷⣦
|
||||
// ⠀⠀⠠⠀⠠⠀⠀⡀⠘⣠⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣊⢇⠠⢐⢄⡙⢿⣿⣿⣾⠫⣻⣿
|
||||
// ⠀⡄⠂⢴⠠⠌⠰⢇⠀⠀⠀⠀⠀⠀⠀⢀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⡀⠣⡣⡱⣌⠻⣿⣿⣿⣿⣿
|
||||
// ⠀⠁⠛⠂⠀⠉⡍⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⠘⣮⣾⡮⢦⡹⣿⣿⣿⣿
|
||||
// ⠄⠠⠉⢼⡇⠶⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⢿⣿⣿⡦⢣⡉⢝⢝⢽
|
||||
// ⠀⠀⠚⠃⠀⠀⡀⠀⠀⠀⠀⣠⣿⣿⣿⣿⣿⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⢸⣿⣿⣿⢀⢃⠀⡁⠑
|
||||
// ⠰⠀⠂⠁⠀⠀⡆⠀⠀⠀⣴⣿⣿⣿⣿⣿⠋⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡈⣿⣿⣿⣇⠆⢃⠈⡇
|
||||
// ⠀⠀⠀⢠⠄⣠⣇⠀⢀⣾⣿⣿⣿⣿⣿⡃⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⣿⣿⣿⣿⢔⢌⢆⢑
|
||||
// ⠀⠀⠀⢀⣶⣿⣷⢀⣾⣿⣿⣿⣿⡯⡊⠀⢸⡏⣟⣻⣽⣭⣽⣛⡛⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡯⡇⣿⣿⣿⣿⣗⢕⢕⠄
|
||||
// ⠀⢠⣶⣿⣿⢿⢃⣾⣿⣿⣿⣿⡫⡪⠀⠀⢸⡇⣿⣿⣿⣿⣿⣿⣿⣦⡘⠄⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡯⡇⣿⣿⣿⣿⣗⢔⢕⢅
|
||||
// ⠀⣺⣿⡉⠕⠁⣼⣿⣿⣿⡿⡫⡪⠂⠀⢔⢸⢀⠸⣿⣿⣿⣿⣿⣿⣿⣿⣶⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣪⠃⣿⣿⣿⣿⡟⢄⣡⣵
|
||||
// ⡛⠟⢋⠀⠀⢸⣿⣿⣿⣿⢠⢫⡊⠀⡔⢵⡈⢠⢣⡹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠡⠚⠓⠪⠉⠛⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡫⡎⣸⣿⣿⣿⡟⢠⢃⣂⡿
|
||||
// ⡃⡊⢃⡔⡠⣿⣿⣿⣿⡇⣜⡘⢀⢜⡀⡅⡆⠘⢐⠁⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢾⠿⢿⣿⣿⣿⣿⣷⣾⣽⡻⢿⣿⣿⣿⣿⣿⣿⣿⡿⣛⣿⣿⣟⡜⠠⣻⣿⣿⡟⠴⠃⠉⠁⠀
|
||||
// ⡺⢠⡾⣸⡷⣿⣿⣿⣿⠂⣓⠂⡎⡺⠪⠒⠃⠀⠀⠀⠀⠀⠙⠿⣯⢻⣿⣿⣿⡟⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣵⣾⣿⡷⣲⣿⣿⣿⣿⣿⣿⣿⣷⣝⢿⣿⣿⣿⡿⡫⡺⣽⣿⣿⡗⠀⢸⣿⣿⠏⠀⠀⠀⠀⠀⠀
|
||||
// ⣴⣿⢣⣿⢳⣿⣿⣿⡿⡨⡊⠠⡪⡢⠂⠀⠀⠀⠀⠀⢀⣀⠀⠀⠘⢧⡻⣿⣿⡇⢿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣿⣿⣵⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢳⣝⠿⡫⡪⣪⡮⣾⣿⡟⢠⠅⣿⠟⠋⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⣿⡏⣾⢣⡾⣿⣿⣿⡇⢪⠂⡨⡠⠀⠀⠀⠀⢀⡴⠋⠉⠈⠉⢦⡀⠠⢅⠹⣿⢰⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢟⣫⣾⡿⣫⣦⠸⢊⡄⣼⣿⡟⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⡿⢸⣏⣾⣇⣿⣿⣿⡇⢵⢀⢓⠀⠀⠀⠀⠀⡮⠀⠀⠀⠀⠀⠈⣇⠀⠧⡗⡈⢿⣺⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⣯⣾⣿⡿⠛⠊⣬⠴⢪⠋⣼⣿⠟⢔⢝⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⠃⡿⣸⣿⣿⢸⣿⣿⡇⡊⢰⢱⢂⡀⠀⠀⠀⢳⠀⠀⠀⠀⠀⢰⡇⠀⣸⢐⡈⢄⢿⠿⠿⢿⡿⠽⢫⣵⣿⡿⠿⠛⠛⠙⠉⠁⠀⠞⠙⠃⠍⡁⠖⡪⡫⡪⣻⢁⣾⡿⢃⢞⢕⡝⢰⣆⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⢸⢣⣿⣿⡗⡈⢿⣿⡇⠀⢸⣿⣷⣵⣄⠀⠀⠈⠲⣠⣠⣠⡴⠋⠀⠀⢒⢔⢂⢀⢎⢇⣓⡕⠂⡚⡩⢡⢰⢂⣓⡣⣢⠒⠀⠀⠀⠀⠀⠀⠀⠈⠢⡈⡲⡑⢡⣾⠟⡠⡣⣂⢇⠃⣿⣿⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⡟⣾⣿⡿⣸⢸⡘⣿⣷⠀⢸⣿⣿⣿⣿⣿⣦⣄⣀⣀⣀⣀⣀⣠⣤⣜⢔⢕⢕⢔⢕⢕⣗⣇⣳⡪⣺⢐⢱⢑⢒⠖⠁⠀⠀⢀⠄⠔⠖⢦⣀⠀⠀⠰⠌⣴⠟⡡⡪⡪⡪⡜⡜⣸⣿⣿⣿⣿⡿⡢⠀⠀⠀⠀⠀⠀
|
||||
// ⢱⣿⣿⡣⢑⠔⡕⠘⣿⡀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣷⣷⣵⣕⣥⣣⣣⡣⡪⣐⢱⢠⡃⠀⠀⠀⣴⠁⠀⠀⠀⠀⠸⣆⠀⠀⠘⠀⡪⡪⡪⡪⡪⣸⢡⣿⣿⣿⣿⣿⡯⣺⡀⢤⡀⠀⠀⠀
|
||||
// ⣿⣿⣟⣝⣝⣺⠁⠀⠘⣧⢘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣷⣇⣣⠣⡘⡀⠀⠀⠀⡟⠀⠀⠀⠀⠀⠨⡗⠀⠀⠀⠈⢮⠺⡪⢊⢫⠃⣾⣿⣿⣿⣿⣿⡪⡏⣦⡀⢹⣷⣤⣀
|
||||
// ⣿⣿⢑⢇⢆⠆⠀⠀⣸⣎⠸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣮⣧⣀⠀⠀⢻⡀⠀⠀⠀⢀⡼⠃⠀⠀⠀⠀⠸⢘⢜⡐⡕⣸⣿⣿⣿⣿⣿⣃⣟⣱⣿⣿⣧⢻⣿⣿
|
||||
// ⣿⣟⢕⢕⢼⠀⠀⠀⣿⣿⣧⡹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⢡⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠉⠓⠒⠓⠋⠀⠀⠀⠀⠀⠠⠀⠱⡱⡨⢣⣿⣿⣿⣿⣿⢗⡯⣱⣿⣿⣿⢇⣄⠃⠙
|
||||
// ⣿⡒⡢⡢⡃⠀⠀⠀⣿⣿⣿⣷⣝⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣤⣄⣀⣀⣀⣀⢀⣀⡀⡠⣀⡢⡪⠂⣽⣿⣿⣿⣿⣟⡕⣺⣿⣿⣿⣏⣿⣷⢔⡄
|
||||
// ⡿⡸⢰⢔⠂⠀⠀⠀⢿⣿⣿⣿⣿⣯⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣯⢮⡊⡠⣰⣿⣿⣿⣿⡿⣡⣿⣿⣿⣿⡟⣾⣿⣿⢵⠅
|
||||
// ⣟⢕⢝⣗⠀⠀⠀⡹⡘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣫⡾⢱⣿⣿⣿⣿⡟⣼⣿⣿⣿⣿⡿⣹⣿⣿⡟⡹⢰
|
||||
// ⢕⢕⢕⢕⠠⡢⣪⢂⢇⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣫⣾⣿⢣⣿⣿⣿⣿⣿⣻⣿⣿⣿⣿⡿⣱⣿⣿⣿⡫⡇⣼
|
||||
// ⢕⢕⢕⠅⣘⡪⡚⡔⠍⠂⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⣫⣾⣿⢟⢁⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢣⣿⣿⣿⣏⡼⢠⣿
|
||||
// ⢕⢕⢕⠀⣖⡪⡪⣂⠀⠐⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢟⣫⣵⡿⢟⣫⡴⢁⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⣸⣿⣿⣿⢒⠇⣼⣿
|
||||
// ⢕⢅⢕⠨⣃⢪⢨⢂⠀⠀⠀⠈⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⣳⢰⢬⡙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣍⣛⠻⠿⠿⠿⢛⣯⡭⠶⣞⣛⣭⣵⣾⣿⠿⢁⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⢀⣿⣿⣿⡯⡝⢰⣿⣿
|
||||
// ⢝⢕⠇⢸⠱⡑⡁⠀⣀⠀⠀⠀⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣷⣬⣃⣹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣾⣿⣿⣿⣿⣿⣿⣿⡿⡱⢃⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠃⠀⣼⣿⣿⣿⣸⢡⣿⣿⣿
|
||||
// ⢕⢕⠅⢜⠕⡠⣢⣾⣿⣷⣤⣄⢀⣬⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢛⠴⢡⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠁⠀⢠⣿⣿⣿⢝⢡⣿⣿⣿⣿
|
||||
// ⢕⢕⠅⣕⢥⡶⢀⠻⣿⣿⣿⣟⢿⣿⣿⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢫⡐⣕⢡⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⠀⠀⢀⣿⣿⣿⠏⢁⣿⣿⣿⣿⣿
|
||||
// ⢕⢕⢠⢗⣛⣓⣁⢱⣾⣿⣿⣿⣷⣷⣽⣿⡻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢛⢔⠕⠓⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠀⠀⢀⣾⣿⡿⡣⢎⣿⣿⣿⣿⣿⣿
|
||||
// ⡕⡕⠠⡅⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡝⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠋⠐⣁⡴⡴⢣⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠃⠀⠀⢠⣾⠿⣫⠞⣱⣿⣿⣿⣿⣿⠟⡡
|
||||
// ⢅⢇⢘⢕⢍⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣎⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠿⠛⠛⠛⠉⠁⠀⢀⢜⢕⢅⠕⣡⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⠀⠐⡫⡠⠜⣡⣾⣿⣿⡿⠟⠫⠦⠪⠊
|
||||
// ⢕⢕⢸⣵⣷⣾⣿⣿⣿⣿⣟⢿⣿⣿⣿⣿⣿⣿⣿⣦⣤⡩⣉⣉⡩⣉⣉⠉⠉⠈⠀⠀⠀⠀⠀⠀⠀⢀⣀⡀⠀⠀⣤⢔⢗⢕⠕⣰⣿⣿⣿⣿⣿⣿⣿⣿⡿⢋⢔⢕⠕⣓⠏⣊⠰⠻⠟⠛⣉⠅⢄⠂⡪⡪⡪⠨
|
||||
//
|
||||
// ~it actually won't be her, but one can dream~
|
||||
//
|
||||
}
|
||||
|
||||
Class CompanionLamp : Actor
|
||||
{
|
||||
Vector3 Trail;
|
||||
Array<LampMoth> moff;
|
||||
Actor parent;
|
||||
bool justteleport;
|
||||
|
||||
Default
|
||||
{
|
||||
Tag "$T_LAMP";
|
||||
+NOGRAVITY;
|
||||
+NOTELEPORT;
|
||||
+DONTSPLASH;
|
||||
+INTERPOLATEANGLES;
|
||||
+LOOKALLAROUND;
|
||||
+FRIENDLY;
|
||||
+NOBLOCKMONST;
|
||||
Radius 4;
|
||||
Height 16;
|
||||
}
|
||||
// random chance to spawn moths
|
||||
void A_Moth()
|
||||
{
|
||||
// count up
|
||||
special1++;
|
||||
for ( int i=0; i<moff.Size(); i++ )
|
||||
{
|
||||
if ( moff[i] && (moff[i].lamp == self) && moff[i].isEntranced() ) continue;
|
||||
moff.Delete(i);
|
||||
i--;
|
||||
}
|
||||
if ( (special1%35) || Random[Moth](0,9) || (moff.Size() >= 30) ) return;
|
||||
// spawn a moth at a random offset
|
||||
double ang = FRandom[Moth](0,360);
|
||||
double pt = FRandom[Moth](-30,30);
|
||||
double dist = FRandom[Moth](10,30);
|
||||
Vector3 ofs = SWWMUtility.Vec3FromAngles(ang,pt)*dist;
|
||||
Vector3 spawnpos = level.Vec3Offset(Vec3Offset(0,0,height/2),ofs);
|
||||
if ( !level.IsPointInLevel(spawnpos) ) return;
|
||||
// higher chance of white moths if carrying the Mashiro plush
|
||||
//int mchance = parent.FindInventory("MothPlushy")?3:9;
|
||||
let m = LampMoth(Spawn(Random[Moth](0,9)?"LampMoth":"LampMoth2",spawnpos));
|
||||
if ( !m.TestMobjLocation() )
|
||||
{
|
||||
m.Destroy();
|
||||
return;
|
||||
}
|
||||
let s = Spawn("SWWMSmallSmoke",m.pos);
|
||||
s.alpha *= .3;
|
||||
m.master = parent;
|
||||
m.lamp = self;
|
||||
m.trail = m.pos;
|
||||
moff.Push(m);
|
||||
SWWMUtility.AchievementProgressInc("moth",1,parent.player);
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
if ( !parent || !SWWMLamp(master) )
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
Spawn("SWWMItemFog",pos);
|
||||
Trail = pos;
|
||||
}
|
||||
|
||||
override void Tick()
|
||||
{
|
||||
Super.Tick();
|
||||
if ( !parent || !SWWMLamp(master) )
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
if ( isFrozen() || (freezetics > 0) ) return;
|
||||
// update trailing position
|
||||
bool foundspot = false;
|
||||
for ( int i=0; i<180; i+=5 )
|
||||
{
|
||||
for ( int j=1; j>=-1; j-=2 )
|
||||
{
|
||||
double ang = (parent.angle-180)+i*j;
|
||||
Vector3 testpos = level.Vec3Offset(parent.pos,SWWMUtility.RotateVector3((32,0,parent.height-8+1.5*sin(level.maptime*3.)),ang));
|
||||
if ( !level.IsPointInLevel(testpos) ) continue;
|
||||
Vector3 oldpos = pos;
|
||||
Vector3 oldprev = prev;
|
||||
Actor oldblockingmobj = blockingmobj;
|
||||
Line oldblockingline = blockingline;
|
||||
Sector oldblockingfloor = blockingfloor, oldblockingceiling = blockingceiling;
|
||||
SetOrigin(testpos,false);
|
||||
if ( !TestMobjLocation() || SWWMUtility.BlockingLineIsBlocking(self,Line.ML_BLOCKING|Line.ML_BLOCKEVERYTHING) || BlockingFloor || BlockingCeiling )
|
||||
{
|
||||
SetOrigin(oldpos,false);
|
||||
prev = oldprev;
|
||||
blockingmobj = oldblockingmobj;
|
||||
blockingline = oldblockingline;
|
||||
blockingfloor = oldblockingfloor;
|
||||
blockingceiling = oldblockingceiling;
|
||||
continue;
|
||||
}
|
||||
SetOrigin(oldpos,false);
|
||||
prev = oldprev;
|
||||
blockingmobj = oldblockingmobj;
|
||||
blockingline = oldblockingline;
|
||||
blockingfloor = oldblockingfloor;
|
||||
blockingceiling = oldblockingceiling;
|
||||
Trail = testpos;
|
||||
foundspot = true;
|
||||
}
|
||||
// check at most for a 45 degree offset
|
||||
if ( foundspot && (i > 45) ) break;
|
||||
}
|
||||
Vector3 diff = level.Vec3Diff(pos,parent.pos);
|
||||
if ( (diff.length() > 400) || justteleport )
|
||||
{
|
||||
Vector3 rel = level.Vec3Diff(pos,trail);
|
||||
justteleport = false;
|
||||
Actor f = Spawn("SWWMItemFog",pos);
|
||||
f.A_StartSound("lamp/disappear",CHAN_VOICE);
|
||||
// carry over the moths
|
||||
foreach ( m:moff )
|
||||
{
|
||||
if ( !m ) continue;
|
||||
Vector3 whereto = level.Vec3Offset(m.pos,rel);
|
||||
if ( !level.IsPointInLevel(whereto) )
|
||||
continue;
|
||||
Vector3 oldp = m.pos;
|
||||
m.SetOrigin(whereto,false);
|
||||
if ( !m.TestMobjLocation() )
|
||||
m.SetOrigin(oldp,false);
|
||||
}
|
||||
SetOrigin(trail,false);
|
||||
angle = AngleTo(parent);
|
||||
vel *= 0.;
|
||||
f = Spawn("SWWMItemFog",pos);
|
||||
f.A_StartSound("lamp/appear",CHAN_VOICE);
|
||||
return;
|
||||
}
|
||||
angle += Clamp(deltaangle(angle,AngleTo(parent)),-5.,5.);
|
||||
vel *= .8;
|
||||
bool blocked = false;
|
||||
if ( SWWMUtility.BlockingLineIsBlocking(self,Line.ML_BLOCKING|Line.ML_BLOCKEVERYTHING) )
|
||||
{
|
||||
// push away from wall
|
||||
Vector3 normal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
|
||||
if ( !SWWMUtility.PointOnLineSide(pos.xy,BlockingLine) ) normal *= -1;
|
||||
vel += 4.*normal;
|
||||
blocked = true;
|
||||
}
|
||||
if ( BlockingFloor )
|
||||
{
|
||||
// push away from floor
|
||||
Vector3 normal = BlockingFloor.floorplane.Normal;
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<BlockingFloor.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(BlockingFloor.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
||||
if ( !(BlockingFloor.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
||||
normal = -BlockingFloor.Get3DFLoor(i).top.Normal;
|
||||
break;
|
||||
}
|
||||
vel += 4.*normal;
|
||||
blocked = true;
|
||||
}
|
||||
if ( BlockingCeiling )
|
||||
{
|
||||
// push away from ceiling
|
||||
Vector3 normal = BlockingCeiling.ceilingplane.Normal;
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<BlockingCeiling.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(BlockingCeiling.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
||||
if ( !(BlockingCeiling.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
||||
normal = -BlockingCeiling.Get3DFloor(i).bottom.Normal;
|
||||
break;
|
||||
}
|
||||
vel += 4.*normal;
|
||||
blocked = true;
|
||||
}
|
||||
if ( (diff.x > -16) && (diff.x < 16) && (diff.y > -16) && (diff.y < 16) && (diff.z > -16) && (diff.z < parent.height+8) )
|
||||
{
|
||||
if ( diff.x < 0 ) vel.x -= .2;
|
||||
else vel.x += .2;
|
||||
if ( diff.y < 0 ) vel.y -= .2;
|
||||
else vel.y += .2;
|
||||
if ( diff.z < 0 ) vel.z -= .2;
|
||||
else vel.z += .2;
|
||||
blocked = true;
|
||||
}
|
||||
if ( blocked ) return;
|
||||
Vector3 dir = level.Vec3Diff(pos,trail);
|
||||
if ( dir.length() > 0 )
|
||||
vel += dir.unit()*min(dir.length()*.05,20.);
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XZW1 A 1
|
||||
{
|
||||
if ( SWWMLamp(master) && SWWMLamp(master).bActive )
|
||||
{
|
||||
A_StartSound("lamp/on",CHAN_ITEMEXTRA,CHANF_OVERLAP);
|
||||
return ResolveState("Active");
|
||||
}
|
||||
return ResolveState(null);
|
||||
}
|
||||
Wait;
|
||||
Active:
|
||||
XZW1 B 1
|
||||
{
|
||||
A_Moth();
|
||||
if ( !SWWMLamp(master) || !SWWMLamp(master).bActive )
|
||||
{
|
||||
A_StartSound("lamp/off",CHAN_ITEMEXTRA,CHANF_OVERLAP);
|
||||
return ResolveState("Spawn");
|
||||
}
|
||||
return ResolveState(null);
|
||||
}
|
||||
Wait;
|
||||
}
|
||||
}
|
||||
|
||||
Class SWWMLamp : Inventory
|
||||
{
|
||||
Mixin SWWMOverlapPickupSound;
|
||||
Mixin SWWMUseToPickup;
|
||||
Mixin SWWMRespawn;
|
||||
Mixin SWWMPickupGlow;
|
||||
|
||||
bool bActive, bActivated;
|
||||
TextureID OnIcon;
|
||||
Actor thelamp;
|
||||
int charge;
|
||||
|
||||
Property Charge : charge;
|
||||
|
||||
override Inventory CreateCopy( Actor other )
|
||||
{
|
||||
// additional lore
|
||||
SWWMLoreLibrary.Add(other.player,"MothLamp");
|
||||
return Super.CreateCopy(other);
|
||||
}
|
||||
override bool HandlePickup( Inventory item )
|
||||
{
|
||||
// add charge
|
||||
if ( item.GetClass() == GetClass() )
|
||||
{
|
||||
if ( (Charge >= Default.Charge) && (Amount+item.Amount > MaxAmount) )
|
||||
{
|
||||
// sell excess
|
||||
int sellprice = abs(Stamina)/2;
|
||||
SWWMScoreObj.Spawn(sellprice,level.Vec3Offset(Owner.pos,SWWMUtility.Vec3FromAngles(FRandom[ScoreBits](0,360),FRandom[ScoreBits](-90,90))*8.+(0,0,Owner.Height/2)));
|
||||
SWWMCredits.Give(Owner.player,sellprice);
|
||||
if ( Owner.player )
|
||||
{
|
||||
if ( Owner.player == players[consoleplayer] ) Console.Printf(StringTable.Localize(SWWMUtility.SellFemaleItem(item)?"$SWWM_SELLEXTRA_FEM":"$SWWM_SELLEXTRA"),GetTag(),sellprice);
|
||||
else Console.Printf(StringTable.Localize(SWWMUtility.SellFemaleItem(item)?"$SWWM_SELLEXTRAREM_FEM":"$SWWM_SELLEXTRAREM"),Owner.player.GetUserName(),GetTag(),sellprice);
|
||||
}
|
||||
}
|
||||
else if ( Charge > 0 )
|
||||
{
|
||||
int AddCharge = Charge+SWWMLamp(item).Charge;
|
||||
Charge = min(Default.Charge,AddCharge);
|
||||
// if there's charge to spare, increase amount
|
||||
if ( AddCharge > Charge )
|
||||
{
|
||||
if ( (Amount > 0) && (Amount+item.Amount < 0) ) Amount = int.max;
|
||||
Amount = min(MaxAmount,Amount+item.Amount);
|
||||
Charge = AddCharge-Charge;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( (Amount > 0) && (Amount+item.Amount < 0) ) Amount = int.max;
|
||||
// new copy, increase and take its charge
|
||||
Amount = min(MaxAmount,Amount+item.Amount);
|
||||
Charge = SWWMLamp(item).Charge;
|
||||
}
|
||||
item.bPickupGood = true;
|
||||
return true;
|
||||
}
|
||||
return Super.HandlePickup(item);
|
||||
}
|
||||
override bool Use( bool pickup )
|
||||
{
|
||||
if ( pickup && !deathmatch ) return false;
|
||||
bActivated = true;
|
||||
bActive = !bActive;
|
||||
if ( !OnIcon ) OnIcon = TexMan.CheckForTexture("graphics/HUD/Icons/I_Lamp.png");
|
||||
Icon = bActive?OnIcon:default.Icon;
|
||||
// don't consume on use
|
||||
Amount++;
|
||||
return true;
|
||||
}
|
||||
override bool ShouldSpawn()
|
||||
{
|
||||
if ( deathmatch ) return false;
|
||||
return Super.ShouldSpawn();
|
||||
}
|
||||
override void PreTravelled()
|
||||
{
|
||||
// remove the lamp
|
||||
if ( thelamp ) thelamp.Destroy();
|
||||
}
|
||||
override void DoEffect()
|
||||
{
|
||||
Super.DoEffect();
|
||||
if ( !thelamp && bActivated )
|
||||
{
|
||||
thelamp = Spawn("CompanionLamp",level.Vec3Offset(Owner.pos,SWWMUtility.RotateVector3((20,0,24),Owner.angle)));
|
||||
CompanionLamp(thelamp).parent = Owner;
|
||||
thelamp.master = self;
|
||||
let f = Spawn("SWWMItemFog",thelamp.pos);
|
||||
f.A_StartSound("lamp/appear",CHAN_VOICE);
|
||||
}
|
||||
if ( bActive && !(level.maptime%35) && !isFrozen() ) Charge--;
|
||||
if ( Charge <= 0 )
|
||||
{
|
||||
Amount--;
|
||||
if ( Amount <= 0 ) DepleteOrDestroy();
|
||||
else Charge = default.Charge;
|
||||
}
|
||||
}
|
||||
override void DetachFromOwner()
|
||||
{
|
||||
Super.DetachFromOwner();
|
||||
if ( thelamp )
|
||||
{
|
||||
let f = Spawn("SWWMItemFog",thelamp.pos);
|
||||
f.A_StartSound("lamp/disappear",CHAN_VOICE);
|
||||
thelamp.Destroy();
|
||||
}
|
||||
Icon = default.Icon;
|
||||
bActive = false;
|
||||
bActivated = false;
|
||||
}
|
||||
clearscope bool isBlinking() const
|
||||
{
|
||||
return ( (Charge < 10) && (level.maptime&8) );
|
||||
}
|
||||
Default
|
||||
{
|
||||
Tag "$T_LAMP";
|
||||
Inventory.Icon "graphics/HUD/Icons/I_LampOff.png";
|
||||
Inventory.PickupSound "misc/p_pkup";
|
||||
Inventory.PickupMessage "$I_LAMP";
|
||||
Inventory.Amount 1;
|
||||
Inventory.MaxAmount 5;
|
||||
Inventory.InterHubAmount 5;
|
||||
Inventory.PickupFlash "SWWMPurplePickupFlash";
|
||||
+INVENTORY.ALWAYSPICKUP;
|
||||
+INVENTORY.AUTOACTIVATE;
|
||||
+INVENTORY.INVBAR;
|
||||
+COUNTITEM;
|
||||
+INVENTORY.BIGPOWERUP;
|
||||
+FLOATBOB;
|
||||
+DONTGIB;
|
||||
FloatBobStrength 0.25;
|
||||
SWWMLamp.Charge 100;
|
||||
Stamina 70000;
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XZW1 A -1;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
840
zscript/items/swwm_miscitems.zsc
Normal file
840
zscript/items/swwm_miscitems.zsc
Normal file
|
|
@ -0,0 +1,840 @@
|
|||
// Other items
|
||||
|
||||
Class Omnisight : Inventory
|
||||
{
|
||||
Mixin SWWMOverlapPickupSound;
|
||||
Mixin SWWMUseToPickup;
|
||||
Mixin SWWMRespawn;
|
||||
|
||||
override bool TryPickup( in out Actor toucher )
|
||||
{
|
||||
if ( !level.allmap )
|
||||
{
|
||||
Actor rt = toucher;
|
||||
if ( toucher.player ) rt = toucher.player.mo;
|
||||
if ( rt.player == players[consoleplayer] )
|
||||
{
|
||||
rt.A_StartSound("powerup/omnisight",CHAN_ITEMEXTRA,CHANF_OVERLAP);
|
||||
// automatically zoom out so the player can know how far this goes
|
||||
CVar.FindCVar('swwm_mm_zoom').SetFloat(2.);
|
||||
}
|
||||
if ( rt is 'Demolitionist' )
|
||||
Demolitionist(rt).lastbump *= 1.1;
|
||||
level.allmap = true;
|
||||
// activate all interest markers
|
||||
let ti = ThinkerIterator.Create("SWWMInterestMarker",STAT_MAPMARKER);
|
||||
Actor a;
|
||||
while ( a = Actor(ti.Next()) ) a.bDORMANT = false;
|
||||
// "spread" to all players
|
||||
for ( int i=0; i<MAXPLAYERS; i++ )
|
||||
{
|
||||
if ( !playeringame[i] || !players[i].mo || (players[i] == rt.player) ) continue;
|
||||
if ( i == consoleplayer )
|
||||
{
|
||||
Console.Printf(StringTable.Localize("$D_OMNISHARE"),rt.player.GetUserName());
|
||||
players[i].mo.A_StartSound("powerup/omnisight",CHAN_ITEMEXTRA,CHANF_OVERLAP);
|
||||
// automatically zoom out so the player can know how far this goes
|
||||
CVar.FindCVar('swwm_mm_zoom').SetFloat(2.);
|
||||
}
|
||||
if ( players[i].mo is 'Demolitionist' )
|
||||
Demolitionist(players[i].mo).lastbump *= 1.1;
|
||||
}
|
||||
}
|
||||
GoAwayAndDie();
|
||||
return true;
|
||||
}
|
||||
Default
|
||||
{
|
||||
Tag "$T_OMNISIGHT";
|
||||
Inventory.PickupSound "misc/p_pkup";
|
||||
Inventory.PickupMessage "$I_OMNISIGHT";
|
||||
Inventory.RestrictedTo "Demolitionist";
|
||||
Inventory.PickupFlash "SWWMPurplePickupFlash";
|
||||
+INVENTORY.ALWAYSPICKUP;
|
||||
+COUNTITEM;
|
||||
+INVENTORY.BIGPOWERUP;
|
||||
+FLOATBOB;
|
||||
+NOTDMATCH;
|
||||
+DONTGIB;
|
||||
FloatBobStrength 0.25;
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XZW1 A -1;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
// 1.1 Safety Tether
|
||||
|
||||
Class SafetyTether : Inventory
|
||||
{
|
||||
Mixin SWWMAutoUseFix;
|
||||
Mixin SWWMOverlapPickupSound;
|
||||
Mixin SWWMUseToPickup;
|
||||
Mixin SWWMRespawn;
|
||||
|
||||
bool bPrimed, bFailed;
|
||||
int primetim;
|
||||
TextureID OnIcon[3];
|
||||
|
||||
override void Travelled()
|
||||
{
|
||||
Super.Travelled();
|
||||
bPrimed = false;
|
||||
}
|
||||
override Inventory CreateTossable( int amt )
|
||||
{
|
||||
if ( bPrimed ) return null; // can't drop while primed
|
||||
let ret = Super.CreateTossable(amt);
|
||||
// reattach our glow if we became a pickup
|
||||
if ( (ret == self) && (PickupFlash is 'SWWMPickupFlash') )
|
||||
{
|
||||
let p = Spawn(PickupFlash,pos);
|
||||
p.target = self;
|
||||
p.SetStateLabel("Pickup");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
override void DoEffect()
|
||||
{
|
||||
Super.DoEffect();
|
||||
if ( !bPrimed ) return;
|
||||
Icon = ((primetim%4)>=2)?default.Icon:(primetim>=6)?(bFailed?OnIcon[1]:OnIcon[2]):OnIcon[0];
|
||||
if ( !bFailed && (primetim == 20) ) Owner.A_StartSound("hahaha/hahaha",CHAN_POWERUP,CHANF_OVERLAP);
|
||||
primetim++;
|
||||
if ( (primetim <= 20) || (!bFailed && (primetim <= 50)) ) return;
|
||||
primetim = 0;
|
||||
bPrimed = false;
|
||||
Icon = default.Icon;
|
||||
if ( bFailed ) return;
|
||||
Vector3 safepos;
|
||||
double safeangle;
|
||||
if ( deathmatch ) [safepos, safeangle] = level.PickDeathmatchStart();
|
||||
else [safepos, safeangle] = level.PickPlayerStart(Owner.PlayerNumber());
|
||||
if ( !level.useplayerstartz ) safepos.z = ONFLOORZ;
|
||||
Vector3 oldpos = Owner.pos;
|
||||
if ( !Owner.Teleport(safepos,safeangle,0) )
|
||||
{
|
||||
if ( Owner is 'Demolitionist' )
|
||||
Demolitionist(Owner).lastbump *= .95;
|
||||
Owner.A_StartSound("powerup/tethererror",CHAN_ITEMEXTRA,CHANF_OVERLAP);
|
||||
if ( Owner.player == players[consoleplayer] ) Console.Printf(StringTable.Localize("$D_TETHERFAIL"));
|
||||
return;
|
||||
}
|
||||
let s = Spawn("DemolitionistShockwave",oldpos);
|
||||
s.target = Owner;
|
||||
s.special1 = 120;
|
||||
s.A_AlertMonsters(0,AMF_EMITFROMTARGET);
|
||||
s = Spawn("DemolitionistShockwave",Owner.pos);
|
||||
s.target = Owner;
|
||||
s.special1 = 120;
|
||||
s.A_AlertMonsters(0,AMF_EMITFROMTARGET);
|
||||
if ( Owner.player == players[consoleplayer] )
|
||||
{
|
||||
Owner.A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP);
|
||||
Owner.A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,pitch:.7);
|
||||
Owner.A_StartSound("demolitionist/hardland",CHAN_FOOTSTEP,CHANF_OVERLAP,pitch:.4);
|
||||
}
|
||||
SWWMHandler.DoFlash(Owner,Color(255,255,255,255),10);
|
||||
SWWMHandler.DoFlash(Owner,Color(255,128,192,255),30);
|
||||
Owner.GiveBody(100,100);
|
||||
SWWMUtility.AchievementProgressInc("sneaky",1,Owner.player);
|
||||
Amount--;
|
||||
if ( Amount <= 0 ) DepleteOrDestroy();
|
||||
}
|
||||
|
||||
override bool Use( bool pickup )
|
||||
{
|
||||
if ( pickup || bPrimed ) return false;
|
||||
if ( !OnIcon[0] ) OnIcon[0] = TexMan.CheckForTexture("graphics/HUD/Icons/I_SafetyOn.png");
|
||||
if ( !OnIcon[1] ) OnIcon[1] = TexMan.CheckForTexture("graphics/HUD/Icons/I_SafetyNo.png");
|
||||
if ( !OnIcon[2] ) OnIcon[2] = TexMan.CheckForTexture("graphics/HUD/Icons/I_SafetyYes.png");
|
||||
bPrimed = true;
|
||||
primetim = 0;
|
||||
Vector3 safepos;
|
||||
if ( deathmatch ) safepos = level.PickDeathmatchStart();
|
||||
else safepos = level.PickPlayerStart(Owner.PlayerNumber());
|
||||
if ( !level.useplayerstartz )
|
||||
{
|
||||
// find the floor for proper distance check
|
||||
let s = level.PointInSector(safepos.xy);
|
||||
safepos.z = s.floorplane.ZAtPoint(safepos.xy);
|
||||
}
|
||||
bFailed = (level.Vec3Diff(Owner.pos,safepos).length() < 400);
|
||||
if ( (Owner.player == players[consoleplayer]) || bBigPowerup ) Owner.A_StartSound(bFailed?"powerup/tetherfail":"powerup/tetheruse",CHAN_ITEMEXTRA,CHANF_OVERLAP);
|
||||
// don't consume on use, will happen later
|
||||
Amount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
Default
|
||||
{
|
||||
Tag "$T_SAFETY";
|
||||
Stamina 240000;
|
||||
Inventory.Icon "graphics/HUD/Icons/I_Safety.png";
|
||||
Inventory.PickupSound "misc/p_pkup";
|
||||
Inventory.UseSound "";
|
||||
Inventory.PickupMessage "$T_SAFETY";
|
||||
Inventory.MaxAmount 5;
|
||||
Inventory.InterHubAmount 5;
|
||||
Inventory.PickupFlash "SWWMPurplePickupFlash";
|
||||
+INVENTORY.ALWAYSPICKUP;
|
||||
+INVENTORY.AUTOACTIVATE;
|
||||
+INVENTORY.INVBAR;
|
||||
+COUNTITEM;
|
||||
+INVENTORY.BIGPOWERUP;
|
||||
+FLOATBOB;
|
||||
+DONTGIB;
|
||||
FloatBobStrength 0.25;
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XZW1 A 33;
|
||||
XZW1 B 2;
|
||||
Loop;
|
||||
}
|
||||
}
|
||||
|
||||
// TBD 1.4 Ballsy Bomb
|
||||
|
||||
Class BallImpact : SWWMNonInteractiveActor
|
||||
{
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
A_QuakeEx(3,3,3,12,0,200,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:100,rollIntensity:.3);
|
||||
A_StartSound("leadball/hit",CHAN_VOICE);
|
||||
A_SprayDecal("WallCrack",-20);
|
||||
int numpt = Random[Spreadgun](5,10);
|
||||
Vector3 x = SWWMUtility.Vec3FromAngles(angle,pitch);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = (x+SWWMUtility.Vec3FromAngles(FRandom[Spreadgun](0,360),FRandom[Spreadgun](-90,90))*.8).unit()*FRandom[Spreadgun](.1,1.2);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,1,1)*Random[Spreadgun](128,192));
|
||||
}
|
||||
numpt = Random[Spreadgun](4,12);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[Spreadgun](0,360),FRandom[Spreadgun](-90,90))*FRandom[Spreadgun](2,8);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[Spreadgun](4,8);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[Spreadgun](0,360),FRandom[Spreadgun](-90,90))*FRandom[Spreadgun](2,8);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Class TheBall : Actor
|
||||
{
|
||||
Mixin SWWMMissileFix;
|
||||
|
||||
double heat;
|
||||
int deadtimer;
|
||||
Vector3 oldvel;
|
||||
Actor lasthit;
|
||||
|
||||
Default
|
||||
{
|
||||
Obituary "$O_LEADBALL";
|
||||
PROJECTILE;
|
||||
+BOUNCEONWALLS;
|
||||
+BOUNCEONFLOORS;
|
||||
+BOUNCEONCEILINGS;
|
||||
+CANBOUNCEWATER;
|
||||
+USEBOUNCESTATE;
|
||||
+DONTBOUNCEONSKY;
|
||||
+NODAMAGETHRUST;
|
||||
+ALLOWBOUNCEONACTORS;
|
||||
+DONTBOUNCEONSHOOTABLES;
|
||||
+HITOWNER;
|
||||
-NOGRAVITY;
|
||||
Speed 80;
|
||||
Gravity 0.1;
|
||||
BounceFactor 1.0;
|
||||
Radius 2;
|
||||
Height 4;
|
||||
}
|
||||
override int SpecialMissileHit( Actor victim )
|
||||
{
|
||||
if ( (vel.length() <= 5) || ((victim == target) && !bHITOWNER) || (victim == lasthit) || (!victim.bSHOOTABLE && !victim.bSOLID) )
|
||||
return 1;
|
||||
// check if we should rip or bounce
|
||||
// girthitude
|
||||
double girth = (victim.radius+victim.height)/2.*max(50,victim.mass)*(victim.health/double(victim.GetSpawnHealth()));
|
||||
// how hard this damn thing is going to slam
|
||||
double slamforce = vel.length()*350.+heat*120;
|
||||
int dmg = int(vel.length()*4.5+heat*45);
|
||||
bool is_schutt = victim.bSHOOTABLE;
|
||||
// critical hit!
|
||||
bool crit = false;
|
||||
if ( is_schutt && !Random[Spreadgun](0,9) )
|
||||
{
|
||||
Spawn("SWWMItemFog",pos);
|
||||
int whichclonk = Random[Spreadgun](1,11);
|
||||
String snd = String.Format("misc/clonk%d",whichclonk);
|
||||
A_AlertMonsters(swwm_uncapalert?0:2500,AMF_EMITFROMTARGET);
|
||||
A_StartSound(snd,CHAN_VOICE,CHANF_OVERLAP,1.,.2);
|
||||
A_StartSound(snd,CHAN_VOICE,CHANF_OVERLAP,1.,.2);
|
||||
victim.A_QuakeEx(8,8,8,8,0,3000,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:300,rollIntensity:1.);
|
||||
victim.A_StartSound(snd,CHAN_DAMAGE,CHANF_OVERLAP,1.,.2);
|
||||
slamforce *= 4;
|
||||
dmg *= 4;
|
||||
vel *= 1.1;
|
||||
let numpt = Random[Spreadgun](20,30);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[Spreadgun](0,360),FRandom[Spreadgun](-90,90))*FRandom[Spreadgun](1,8);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.scale *= 3.;
|
||||
s.alpha *= .2;
|
||||
s.vel = pvel;
|
||||
}
|
||||
crit = true;
|
||||
if ( target ) SWWMUtility.AchievementProgressInc("balls",1,target.player);
|
||||
}
|
||||
SWWMUtility.DoKnockback(victim,vel.unit(),slamforce);
|
||||
bool bleeds = (victim && !victim.bINVULNERABLE && !victim.bNOBLOOD && !victim.bDORMANT && is_schutt);
|
||||
int newdmg = victim.DamageMobj(self,target,dmg,crit?'CriticalConcussion':'Concussion',crit?(DMG_THRUSTLESS|DMG_FOILINVUL):DMG_THRUSTLESS); // crits ignore invulnerability
|
||||
Vector3 dir = -vel.unit();
|
||||
// slam jam
|
||||
if ( bleeds )
|
||||
{
|
||||
A_StartSound("leadball/hitf",CHAN_VOICE,CHANF_OVERLAP,(vel.length()/30.)**.5);
|
||||
if ( (newdmg > 0) && victim )
|
||||
{
|
||||
victim.A_StartSound("leadball/hitf",CHAN_DAMAGE,CHANF_OVERLAP,(vel.length()/30.)**.5);
|
||||
victim.TraceBleed(newdmg,self);
|
||||
victim.SpawnBlood(pos,atan2(dir.y,dir.x),dmg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
A_StartSound("leadball/hit",CHAN_VOICE,CHANF_OVERLAP,(vel.length()/30.)**.5);
|
||||
if ( victim ) victim.A_StartSound("leadball/hit",CHAN_DAMAGE,CHANF_OVERLAP,(vel.length()/30.)**.5);
|
||||
if ( vel.length() > 15. )
|
||||
{
|
||||
let s = Spawn("BallImpact",pos);
|
||||
s.angle = atan2(dir.y,dir.x);
|
||||
s.pitch = asin(-dir.z);
|
||||
}
|
||||
}
|
||||
// make it so the crit does not propagate to friendlies unless we bonked a friend (you monster!)
|
||||
if ( crit )
|
||||
SWWMUtility.DoExplosion(self,dmg/2,25000,150,80,((victim.isFriend(target))?0:DE_NOHURTFRIEND)|DE_NONEXPLOSIVE,crit?'CriticalConcussion':'Concussion',target,DMG_FOILINVUL);
|
||||
if ( crit && victim && (victim.Health <= 0) && (victim.bBOSS || victim.FindInventory("BossMarker")) && target )
|
||||
SWWMUtility.MarkAchievement("clonk",target.player);
|
||||
// only rip shootables
|
||||
if ( (slamforce > girth) && is_schutt )
|
||||
{
|
||||
vel *= .7;
|
||||
return 1;
|
||||
}
|
||||
// force bounce
|
||||
BlockingMobj = victim;
|
||||
A_HandleBounce();
|
||||
lasthit = victim;
|
||||
// pretend to pass through
|
||||
return 1;
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
A_StartSound("leadball/fly",CHAN_WEAPON,CHANF_LOOP,.6,3.,2.);
|
||||
heat = 1.;
|
||||
}
|
||||
override void Tick()
|
||||
{
|
||||
oldvel = vel;
|
||||
Super.Tick();
|
||||
if ( isFrozen() || (freezetics > 0) ) return;
|
||||
if ( InStateSequence(CurState,ResolveState("Death")) )
|
||||
{
|
||||
deadtimer++;
|
||||
if ( deadtimer > 300 )
|
||||
{
|
||||
let numpt = Random[Spreadgun](3,6);
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[Spreadgun](0,360),FRandom[Spreadgun](-90,90))*FRandom[Spreadgun](1,8);
|
||||
let s = Spawn("SWWMSmallSmoke",pos);
|
||||
s.scale *= 3.;
|
||||
s.alpha *= .2;
|
||||
s.vel = pvel;
|
||||
}
|
||||
Destroy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
heat -= 0.004+0.0004*vel.length();
|
||||
A_SoundVolume(CHAN_WEAPON,vel.length()/75.);
|
||||
if ( heat <= 0 ) return;
|
||||
let s = Spawn("SWWMHalfSmoke",pos);
|
||||
s.alpha *= heat;
|
||||
}
|
||||
void A_HandleBounce()
|
||||
{
|
||||
lasthit = null;
|
||||
Vector3 HitNormal = -vel.unit();
|
||||
F3DFloor ff;
|
||||
if ( BlockingFloor )
|
||||
{
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<BlockingFloor.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(BlockingFloor.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
||||
if ( !(BlockingFloor.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
||||
ff = BlockingFloor.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff ) HitNormal = -ff.top.Normal;
|
||||
else HitNormal = BlockingFloor.floorplane.Normal;
|
||||
}
|
||||
else if ( BlockingCeiling )
|
||||
{
|
||||
// find closest 3d floor for its normal
|
||||
for ( int i=0; i<BlockingCeiling.Get3DFloorCount(); i++ )
|
||||
{
|
||||
if ( !(BlockingCeiling.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
|
||||
if ( !(BlockingCeiling.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
||||
ff = BlockingCeiling.Get3DFloor(i);
|
||||
break;
|
||||
}
|
||||
if ( ff ) HitNormal = -ff.bottom.Normal;
|
||||
else HitNormal = BlockingCeiling.ceilingplane.Normal;
|
||||
}
|
||||
else if ( BlockingLine )
|
||||
{
|
||||
HitNormal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
|
||||
int wside = SWWMUtility.PointOnLineSide(pos.xy,BlockingLine);
|
||||
if ( !wside ) HitNormal *= -1;
|
||||
}
|
||||
else if ( BlockingMobj )
|
||||
{
|
||||
Vector3 diff = level.Vec3Diff(BlockingMobj.Vec3Offset(0,0,BlockingMobj.Height/2),pos);
|
||||
HitNormal = diff.unit();
|
||||
}
|
||||
// send the needed data for a bust
|
||||
if ( (special1 == 2) || swwm_omnibust )
|
||||
{
|
||||
int dmg = int(oldvel.length()*4.2+heat*80);
|
||||
BusterWall.ProjectileBust(self,dmg,oldvel.unit());
|
||||
}
|
||||
// undo the bounce, we need to hook in our own
|
||||
vel = oldvel;
|
||||
// re-do the bounce with our formula
|
||||
double bcefact = .9;
|
||||
if ( BlockingMobj )
|
||||
{
|
||||
bcefact *= .7;
|
||||
if ( !BlockingMobj.bINVULNERABLE && !BlockingMobj.bNOBLOOD && !BlockingMobj.bDORMANT )
|
||||
bcefact *= .6;
|
||||
}
|
||||
vel = (vel dot HitNormal)*HitNormal*FRandom[Spreadgun](-1.8,-1.)+vel;
|
||||
vel += SWWMUtility.Vec3FromAngles(FRandom[Spreadgun](0,360),FRandom[Spreadgun](-90,90))*.4;
|
||||
vel *= bcefact;
|
||||
// slam jam
|
||||
if ( !BlockingMobj )
|
||||
{
|
||||
A_StartSound("leadball/hit",CHAN_VOICE,CHANF_OVERLAP,max(0.,(vel.length()/30.-.2))**.5);
|
||||
if ( vel.length() > 15 )
|
||||
{
|
||||
let s = Spawn("BallImpact",pos);
|
||||
s.angle = atan2(HitNormal.y,HitNormal.x);
|
||||
s.pitch = asin(-HitNormal.z);
|
||||
}
|
||||
}
|
||||
gravity = .35;
|
||||
if ( (vel.length() < 5) && (pos.z <= floorz) )
|
||||
{
|
||||
ClearBounce();
|
||||
ExplodeMissile();
|
||||
}
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XZW1 A -1;
|
||||
Stop;
|
||||
Bounce:
|
||||
XZW1 A 0 A_HandleBounce();
|
||||
Goto Spawn;
|
||||
Death:
|
||||
XZW1 A -1
|
||||
{
|
||||
bMOVEWITHSECTOR = true;
|
||||
A_StopSound(CHAN_WEAPON);
|
||||
}
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
// TBD 1.4 Battle-Boi Sentry
|
||||
|
||||
Class SaltTracer : LineTracer
|
||||
{
|
||||
Actor ignore;
|
||||
Array<Line> ShootThroughList;
|
||||
Array<WaterHit> WaterHitList;
|
||||
|
||||
override ETraceStatus TraceCallback()
|
||||
{
|
||||
// liquid splashes
|
||||
if ( Results.CrossedWater )
|
||||
{
|
||||
let hl = new("WaterHit");
|
||||
hl.sect = Results.CrossedWater;
|
||||
hl.hitpos = Results.CrossedWaterPos;
|
||||
WaterHitList.Push(hl);
|
||||
}
|
||||
else if ( Results.Crossed3DWater )
|
||||
{
|
||||
let hl = new("WaterHit");
|
||||
hl.sect = Results.Crossed3DWater;
|
||||
hl.hitpos = Results.Crossed3DWaterPos;
|
||||
WaterHitList.Push(hl);
|
||||
}
|
||||
if ( Results.HitType == TRACE_HitActor )
|
||||
{
|
||||
if ( Results.HitActor == ignore ) return TRACE_Skip;
|
||||
if ( Results.HitActor.bSHOOTABLE ) return TRACE_Stop;
|
||||
return TRACE_Skip;
|
||||
}
|
||||
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
|
||||
{
|
||||
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&Line.ML_BlockHitscan) )
|
||||
return TRACE_Stop;
|
||||
ShootThroughList.Push(Results.HitLine);
|
||||
return TRACE_Skip;
|
||||
}
|
||||
return TRACE_Stop;
|
||||
}
|
||||
}
|
||||
|
||||
Class SaltLight : PaletteLight
|
||||
{
|
||||
Default
|
||||
{
|
||||
Tag "SaltExpl,1";
|
||||
ReactionTime 30;
|
||||
Args 0,0,0,240;
|
||||
}
|
||||
}
|
||||
Class SaltLight2 : PaletteLight
|
||||
{
|
||||
Default
|
||||
{
|
||||
Tag "SaltExpl";
|
||||
ReactionTime 30;
|
||||
Args 0,0,0,70;
|
||||
}
|
||||
}
|
||||
|
||||
Class SaltImpact : SWWMNonInteractiveActor
|
||||
{
|
||||
Default
|
||||
{
|
||||
Obituary "$O_SALTSHOT";
|
||||
DamageType "Salt";
|
||||
RenderStyle "Add";
|
||||
Scale 1.8;
|
||||
+NODAMAGETHRUST;
|
||||
+FORCERADIUSDMG;
|
||||
+FORCEXYBILLBOARD;
|
||||
+FOILINVUL;
|
||||
}
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
A_AlertMonsters(swwm_uncapalert?0:6000,AMF_EMITFROMTARGET);
|
||||
SWWMUtility.DoExplosion(self,30+special2*4,15000,100,40);
|
||||
A_QuakeEx(3,3,3,10,0,250,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:150,rollintensity:0.2);
|
||||
A_StartSound("saltshot/hit",CHAN_VOICE,attenuation:.35);
|
||||
A_SprayDecal("ShockMarkSmall",-172);
|
||||
A_SprayDecal("SaltMark",-172);
|
||||
Scale *= FRandom[ExploS](0.8,1.1);
|
||||
int numpt = Random[ExploS](5,9)-special1;
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](1,6);
|
||||
let s = Spawn("SWWMSmoke",pos);
|
||||
s.vel = pvel;
|
||||
s.SetShade(Color(1,3,4)*Random[ExploS](48,63));
|
||||
s.special1 = Random[ExploS](1,2);
|
||||
s.scale *= 2.4;
|
||||
s.A_SetRenderStyle(.4,STYLE_AddShaded);
|
||||
}
|
||||
numpt = Random[ExploS](3,9)-special1;
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](2,6);
|
||||
let s = Spawn("SWWMSpark",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
numpt = Random[ExploS](3,6)-special1;
|
||||
for ( int i=0; i<numpt; i++ )
|
||||
{
|
||||
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](2,12);
|
||||
let s = Spawn("SWWMChip",pos);
|
||||
s.vel = pvel;
|
||||
}
|
||||
Spawn("SaltLight2",pos);
|
||||
}
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
TNT1 A 0 NoDelay A_Jump(256,"Expl1","Expl2","Expl3");
|
||||
Expl1:
|
||||
KSX1 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright;
|
||||
Stop;
|
||||
Expl2:
|
||||
KSX2 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright;
|
||||
Stop;
|
||||
Expl3:
|
||||
KSX3 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
|
||||
Class SaltBeam : SWWMNonInteractiveActor
|
||||
{
|
||||
Default
|
||||
{
|
||||
Obituary "$O_SALTSHOT";
|
||||
DamageType "Salt";
|
||||
RenderStyle "Add";
|
||||
Stamina 9;
|
||||
Speed 32;
|
||||
+ROLLSPRITE;
|
||||
+ROLLCENTER;
|
||||
+NODAMAGETHRUST;
|
||||
+FORCERADIUSDMG;
|
||||
+FOILINVUL;
|
||||
}
|
||||
|
||||
void SpreadOut()
|
||||
{
|
||||
special1 = 1;
|
||||
Vector3 x, y, z;
|
||||
[x, y, z] = SWWMUtility.GetAxes(angle,pitch,roll);
|
||||
let t = new("SaltTracer");
|
||||
t.ignore = target;
|
||||
t.Trace(pos,cursector,x,speed,TRACE_HitSky);
|
||||
foreach ( l:t.ShootThroughList )
|
||||
{
|
||||
l.Activate(target,0,SPAC_PCross);
|
||||
l.Activate(target,0,SPAC_Impact);
|
||||
}
|
||||
foreach ( w:t.WaterHitList )
|
||||
{
|
||||
let b = Actor.Spawn("InvisibleSplasher",w.hitpos);
|
||||
b.target = target;
|
||||
b.A_CheckTerrain();
|
||||
}
|
||||
for ( int i=4; i<t.Results.Distance; i+=8 )
|
||||
{
|
||||
if ( Random[Spreadgun](0,Stamina) ) continue;
|
||||
let b = Actor.Spawn("SWWMHalfSmoke",level.Vec3Offset(pos,x*i));
|
||||
b.Scale *= FRandom[Spreadgun](.6,.8);
|
||||
b.special1 = Random[Spreadgun](1,2);
|
||||
b.A_SetRenderStyle(.3,STYLE_AddShaded);
|
||||
b.SetShade(Color(1,3,4)*Random[Spreadgun](48,63));
|
||||
b.vel += x*FRandom[Spreadgun](-.5,3);
|
||||
}
|
||||
if ( t.Results.HitType != TRACE_HitNone )
|
||||
{
|
||||
if ( (args[1] == 2) || swwm_omnibust ) BusterWall.Bust(t.Results,60+Accuracy*10,target,x,t.Results.HitPos.z);
|
||||
if ( t.Results.HitType == TRACE_HitActor )
|
||||
{
|
||||
SWWMUtility.DoKnockback(t.Results.HitActor,x,25000);
|
||||
let p = SWWMPuff.Setup(t.Results.HitPos,x,self,target,t.Results.HitActor);
|
||||
t.Results.HitActor.DamageMobj(p,target,40+Accuracy*5,'Salt',DMG_THRUSTLESS|DMG_INFLICTOR_IS_PUFF);
|
||||
if ( t.Results.HitActor && t.Results.HitActor.bIsMonster && !Random[Spreadgun](0,3) )
|
||||
t.Results.HitActor.Howl();
|
||||
}
|
||||
Vector3 norm = -x;
|
||||
if ( t.Results.HitType == TRACE_HitWall )
|
||||
{
|
||||
norm = (t.Results.HitLine.delta.y,-t.Results.HitLine.delta.x,0).unit();
|
||||
if ( t.Results.Side ) norm *= -1;
|
||||
t.Results.HitLine.RemoteActivate(tracer,t.Results.Side,SPAC_Impact,t.Results.HitPos);
|
||||
}
|
||||
else if ( t.Results.HitType == TRACE_HitFloor )
|
||||
{
|
||||
if ( t.Results.ffloor ) norm = -t.Results.ffloor.top.Normal;
|
||||
else norm = t.Results.HitSector.floorplane.Normal;
|
||||
}
|
||||
else if ( t.Results.HitType == TRACE_HitCeiling )
|
||||
{
|
||||
if ( t.Results.ffloor ) norm = -t.Results.ffloor.bottom.Normal;
|
||||
else norm = t.Results.HitSector.ceilingplane.Normal;
|
||||
}
|
||||
if ( t.Results.HitType != TRACE_HasHitSky )
|
||||
{
|
||||
let i = Spawn("SaltImpact",level.Vec3Offset(t.Results.HitPos,norm*4));
|
||||
i.angle = atan2(norm.y,norm.x);
|
||||
i.pitch = asin(-norm.z);
|
||||
i.target = target;
|
||||
i.special1 = (Stamina-9)/4;
|
||||
i.special2 = Accuracy;
|
||||
i.args[0] = args[1];
|
||||
}
|
||||
speed = t.Results.Distance; // shortens in minimap
|
||||
return;
|
||||
}
|
||||
else if ( (args[0] > 20) && !Random[Spreadgun](0,800/args[0]) )
|
||||
{
|
||||
let i = Spawn("SaltImpact",level.Vec3Offset(pos,x*speed));
|
||||
i.angle = atan2(x.y,x.x);
|
||||
i.pitch = asin(-x.z);
|
||||
i.target = target;
|
||||
i.special1 = (Stamina-9)/4;
|
||||
i.special2 = Accuracy;
|
||||
i.args[0] = args[1];
|
||||
return;
|
||||
}
|
||||
// next beam
|
||||
if ( !(special2%4) && !Random[Spreadgun](0,Stamina) )
|
||||
Spawn("SaltLight",level.Vec3Offset(pos,x*speed/2));
|
||||
let next = Spawn("SaltBeam",level.Vec3Offset(pos,x*speed));
|
||||
double a = FRandom[Spreadgun](0,360), s = FRandom[Spreadgun](0,.06);
|
||||
Vector3 dir = SWWMUtility.ConeSpread(x,y,z,a,s);
|
||||
next.angle = atan2(dir.y,dir.x);
|
||||
next.pitch = asin(-dir.z);
|
||||
next.target = target;
|
||||
next.special2 = (special2+1)%10;
|
||||
next.args[0] = args[0]+1;
|
||||
next.args[1] = args[1];
|
||||
next.SetStateLabel("TrailSpawn");
|
||||
}
|
||||
|
||||
override void PostBeginPlay()
|
||||
{
|
||||
Super.PostBeginPlay();
|
||||
if ( !Random[Spreadgun](0,3) )
|
||||
A_StartSound("saltshot/trail",CHAN_VOICE,CHANF_DEFAULT,.3,4.);
|
||||
}
|
||||
|
||||
override void Tick()
|
||||
{
|
||||
if ( freezetics > 0 )
|
||||
{
|
||||
freezetics--;
|
||||
return;
|
||||
}
|
||||
if ( isFrozen() ) return;
|
||||
A_FadeOut(.04);
|
||||
if ( Random[Spreadgun](-2,args[2]/10) == 0 )
|
||||
SWWMUtility.DoExplosion(self,5+Accuracy,5000,speed,flags:DE_HOWL|DE_NONEXPLOSIVE,ignoreme:target);
|
||||
if ( ((special2%4) || args[2]) && !special1 ) SpreadOut();
|
||||
args[2]++;
|
||||
if ( !CheckNoDelay() || (tics == -1) ) return;
|
||||
if ( tics > 0 ) tics--;
|
||||
while ( !tics )
|
||||
{
|
||||
if ( !SetState(CurState.NextState) )
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
XZW1 A -1 Bright NoDelay
|
||||
{
|
||||
return FindState("StarterDev")+Random[Spreadgun](0,11)*2;
|
||||
}
|
||||
Stop;
|
||||
TrailSpawn:
|
||||
XZW2 A -1 Bright
|
||||
{
|
||||
return FindState("TrailerDev")+Random[Spreadgun](0,11)*2;
|
||||
}
|
||||
Stop;
|
||||
StarterDev:
|
||||
#### # 25 Bright;
|
||||
XZW1 B -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW1 C -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW1 D -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW1 E -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW1 F -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW1 G -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW1 H -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW1 I -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW1 J -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW1 K -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW1 L -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW1 M -1 Bright;
|
||||
Stop;
|
||||
TrailerDev:
|
||||
#### # 25 Bright;
|
||||
XZW2 B -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW2 C -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW2 D -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW2 E -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW2 F -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW2 G -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW2 H -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW2 I -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW2 J -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW2 K -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW2 L -1 Bright;
|
||||
Stop;
|
||||
#### # 25 Bright;
|
||||
XZW2 M -1 Bright;
|
||||
Stop;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
1289
zscript/items/swwm_powerups_vip.zsc
Normal file
1289
zscript/items/swwm_powerups_vip.zsc
Normal file
File diff suppressed because it is too large
Load diff
1586
zscript/weapons/swwm_deathlydeathcannon_altfx.zsc
Normal file
1586
zscript/weapons/swwm_deathlydeathcannon_altfx.zsc
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue