Replication of weapon created from scratch: need help
- Voodoo Doll
- Average
- Posts: 42
- Joined: Wed Aug 03, 2011 2:27 am
- Location: Russian Federation
Replication of weapon created from scratch: need help
Hello all.
I made some sort of rifle that allows shoot through walls, with almost full support of this, that means it can hit the target behind <=20 walls, if there are 64>=x>=128 spaces between walls and these walls are of <=4096 thickness. The rifle have many features, sensing the target through walls and displaying target's face and name is just some of them. The key to wall penetration is projectiles. At grand total rifle have 5 types of projectiles, which created by weapon or by each other. It work well on botmatch or singleplayer game types, i. e. offline. I made attempt to replicate ALL variables and functions I used in rifle, but i didn't reached result. If anyone can help me, I'll be very glad. The main rifle class is well commenced, and also this pack have detailed manual. Explore it, use it for your own implementations of some features, and maybe we'll find way to make this thing working over the network.
Main class: her.herwpn (Actor\Inventory\Weapon\TournamentWeapon\herwpn).
Main projectile classes:
her.herprja (Actor\Projectile\herprjb) - regular;
her.herprjb (Actor\Projectile\herprjb) - fast, used for instant hit.
I made some sort of rifle that allows shoot through walls, with almost full support of this, that means it can hit the target behind <=20 walls, if there are 64>=x>=128 spaces between walls and these walls are of <=4096 thickness. The rifle have many features, sensing the target through walls and displaying target's face and name is just some of them. The key to wall penetration is projectiles. At grand total rifle have 5 types of projectiles, which created by weapon or by each other. It work well on botmatch or singleplayer game types, i. e. offline. I made attempt to replicate ALL variables and functions I used in rifle, but i didn't reached result. If anyone can help me, I'll be very glad. The main rifle class is well commenced, and also this pack have detailed manual. Explore it, use it for your own implementations of some features, and maybe we'll find way to make this thing working over the network.
Main class: her.herwpn (Actor\Inventory\Weapon\TournamentWeapon\herwpn).
Main projectile classes:
her.herprja (Actor\Projectile\herprjb) - regular;
her.herprjb (Actor\Projectile\herprjb) - fast, used for instant hit.
- Attachments
-
- her.zip
- (1.21 MiB) Downloaded 161 times
Last edited by Voodoo Doll on Sat Mar 17, 2012 6:45 pm, edited 1 time in total.
Re: Replication of weapon created from scratch: need help
Pretty fun to play offline. An online version would be a must.
Re: Replication of weapon created from scratch: need help
medor wrote:
no work hidden weapond with it ?
Re: Replication of weapon created from scratch: need help
Will check later, bookmarking.
PS.
If it uses the standard sniper rifle model... there might be the possibility of making the scope show some kind of heat visor (like in red faction with a weapon that did the same).
It involves scripted textures but can totally be done.
PS.
If it uses the standard sniper rifle model... there might be the possibility of making the scope show some kind of heat visor (like in red faction with a weapon that did the same).
It involves scripted textures but can totally be done.
Re: Replication of weapon created from scratch: need help
medor wrote:When i die if i reborn with a rifle i can not fire ;i must ThrowWeapon it and take it for it run.
Re: Replication of weapon created from scratch: need help
Seen it, very interesting indeed.
A few things:
- Weapon first person model is either too big or being drawn too close to player.
- Calling AllActors for Pawns on clients won't locate pawns behind walls.
- Heat sensor should exclude FortStandards
- Firing code is outside of state code, making it harder to understand.
Suggestions:
- Try rendering weapon a little further away.
- If each player is going to call allactors in a crowded server, expect performance drop. Make the AllActors call exclusively for target detection, only in server, and only in a timer (0.2 to 1.0 second).
- Store the 16 most relevant targets as: vector location + PRIpoiner(may exist or not); also, use an extra variable (byte) to count how many relevant targets you have.
- Replicate the locations, the PRIpointers, and the extra variable used to count targets, let the client owning the weapon draw the dots according to the replicated info, if PRI pointer exists, draw the additional data.
- Put your current fire code in NormalFire state!, once done, make a ClientFiring state that does the same but only plays animations (no actual fire or whatsoever).
- Since you're going to be able to fire while pressing alt fire, take the AltFire() and ClientAltFire() from tournament weapon, and remove the line that tells the weapon to go into state AltFiring or ClientAltFiring
- The client should be the one to have full control of the scope, and not the server via a state 'Zooming' that isn't replicated, in PostRender(), make an if to check the PlayerOwner's bAltFire button and use that to draw the zoom, if it wasn't pressed before then reset zoom and scaling system (use bWasZooming).
- You're adding her.herwpnsr insted of her.herwpn in the arena mutator. (default weapon)
In short:
Target detection used in Timer instead and only serverside, store variables (locations, PRIs, NumEnemies (byte) ) for replication.
State NormalFire controlling firing, and state idle when you're not firing.
State ClientFiring doing a simulation of firing (sounds and animations)
Tell AltFire() and ClientAltFire() not to change states, alt firing states won't be used. (try setting ClientAltFire to a blank function if you want)
Use PostRender to detect zoom key (bAltFire on pawn), at the end of post render do (bWasZooming = Pawn(Owner).bAltFire > 0), if in next post render (Pawn(Owner).bAltFire > 0 && !bWasZooming), reset rotation rate and zoom size. (this way you don't need to use state Zooming).
Draw the replicated locations and (with optional) PRIinfos, only draw as much as NumEnemies (replicated as well).
A few things:
- Weapon first person model is either too big or being drawn too close to player.
- Calling AllActors for Pawns on clients won't locate pawns behind walls.
- Heat sensor should exclude FortStandards
- Firing code is outside of state code, making it harder to understand.
Suggestions:
- Try rendering weapon a little further away.
- If each player is going to call allactors in a crowded server, expect performance drop. Make the AllActors call exclusively for target detection, only in server, and only in a timer (0.2 to 1.0 second).
- Store the 16 most relevant targets as: vector location + PRIpoiner(may exist or not); also, use an extra variable (byte) to count how many relevant targets you have.
- Replicate the locations, the PRIpointers, and the extra variable used to count targets, let the client owning the weapon draw the dots according to the replicated info, if PRI pointer exists, draw the additional data.
- Put your current fire code in NormalFire state!, once done, make a ClientFiring state that does the same but only plays animations (no actual fire or whatsoever).
- Since you're going to be able to fire while pressing alt fire, take the AltFire() and ClientAltFire() from tournament weapon, and remove the line that tells the weapon to go into state AltFiring or ClientAltFiring
- The client should be the one to have full control of the scope, and not the server via a state 'Zooming' that isn't replicated, in PostRender(), make an if to check the PlayerOwner's bAltFire button and use that to draw the zoom, if it wasn't pressed before then reset zoom and scaling system (use bWasZooming).
- You're adding her.herwpnsr insted of her.herwpn in the arena mutator. (default weapon)
In short:
Target detection used in Timer instead and only serverside, store variables (locations, PRIs, NumEnemies (byte) ) for replication.
State NormalFire controlling firing, and state idle when you're not firing.
State ClientFiring doing a simulation of firing (sounds and animations)
Tell AltFire() and ClientAltFire() not to change states, alt firing states won't be used. (try setting ClientAltFire to a blank function if you want)
Use PostRender to detect zoom key (bAltFire on pawn), at the end of post render do (bWasZooming = Pawn(Owner).bAltFire > 0), if in next post render (Pawn(Owner).bAltFire > 0 && !bWasZooming), reset rotation rate and zoom size. (this way you don't need to use state Zooming).
Draw the replicated locations and (with optional) PRIinfos, only draw as much as NumEnemies (replicated as well).
Re: Replication of weapon created from scratch: need help
medor wrote:May be help you i have videos tuto for weapons http://unrealtournament.99.free.fr/utfi ... ls-Weapon/
- Voodoo Doll
- Average
- Posts: 42
- Joined: Wed Aug 03, 2011 2:27 am
- Location: Russian Federation
Re: Replication of weapon created from scratch: need help
Higor thanks, I'll try it.
some questions:
1. Why don't keep firing in Idle state? Then weapon will use 2 states only - Pickup and Idle, if I manage to eliminate Zooming state.
2. There were a kinda working a bit network class of rifle in older version of package, called her.herwpnsr, I used it in the mutator replacement function for a time, but now it's removed. Does network version should be a subclass of Botpack.Sniperrifle, like herwpnsr? or i can keep only herwpn class? If so, it will be easier to modify package, since in old versions actually were 2 rifles - herwpn and herwpnsr. Now I want to remove herwpnsr.
3. Over network, weapon won't execute console functions like hertogglecolormode(), hercyclefiremode(). How to fix this?
All
last night i've done new feature, now it's possible to see live feed picture from last fired target tracking projectile. but since drawportal() is buggy (the 1st person mesh rendering twice) I hide the mesh while zooming (picture shown while zooming only). in the next recoded version i'll put this, or i can show the code here, if somebody need it.
some questions:
1. Why don't keep firing in Idle state? Then weapon will use 2 states only - Pickup and Idle, if I manage to eliminate Zooming state.
2. There were a kinda working a bit network class of rifle in older version of package, called her.herwpnsr, I used it in the mutator replacement function for a time, but now it's removed. Does network version should be a subclass of Botpack.Sniperrifle, like herwpnsr? or i can keep only herwpn class? If so, it will be easier to modify package, since in old versions actually were 2 rifles - herwpn and herwpnsr. Now I want to remove herwpnsr.
3. Over network, weapon won't execute console functions like hertogglecolormode(), hercyclefiremode(). How to fix this?
All
last night i've done new feature, now it's possible to see live feed picture from last fired target tracking projectile. but since drawportal() is buggy (the 1st person mesh rendering twice) I hide the mesh while zooming (picture shown while zooming only). in the next recoded version i'll put this, or i can show the code here, if somebody need it.
Re: Replication of weapon created from scratch: need help
Voodoo Doll wrote:Higor thanks, I'll try it.
some questions:
1. Why don't keep firing in Idle state? Then weapon will use 2 states only - Pickup and Idle, if I manage to eliminate Zooming state.
2. There were a kinda working a bit network class of rifle in older version of package, called her.herwpnsr, I used it in the mutator replacement function for a time, but now it's removed. Does network version should be a subclass of Botpack.Sniperrifle, like herwpnsr? or i can keep only herwpn class? If so, it will be easier to modify package, since in old versions actually were 2 rifles - herwpn and herwpnsr. Now I want to remove herwpnsr.
3. Over network, weapon won't execute console functions like hertogglecolormode(), hercyclefiremode(). How to fix this?
All
last night i've done new feature, now it's possible to see live feed picture from last fired target tracking projectile. but since drawportal() is buggy (the 1st person mesh rendering twice) I hide the mesh while zooming (picture shown while zooming only). in the next recoded version i'll put this, or i can show the code here, if somebody need it.
The weapon should be the same item in both client and server, using Firing states make it easy to differ code used in clients and server, or better, to call the same functions in different contexts.
Either way, as it is now, your weapon still tries to go into ClientFiring (and ClientAltFiring) state on clients, if you intend to code it without firing states, do something with ClientFire() in TournamentWeapon.
You should take a look at the Siege Ultimate package, the constructor can execute console commands even if you're don't have it as main weapon, no idea why here but you may learn something.
EDIT:
The Tick method to control firing used is superior to the one built in on UT's weapons, you may want to keep this function as a whole on both server and client, use that conditional to differ what the client version and the server version do:
Code: Select all
simulated function Tick( float Delta)
**common code**check if Pawn(Owner) has bFire>0**
**common code**check for ammo**
if ( Level.netMode != NM_Client )
FireProjectiles();
**common code**play animation**
Code: Select all
Server > Creation of actor
Server > Send actor to client (player must own it on spawn, or actor must be bAlwaysRelevant=true)
Client > Clientside version is automatically spawned, expect PostNetBeginPlay() on simulated actors
Server > Run events and main code
Client > If, specified, start simulation (can run events if RemoteRole == ROLE_SimulatedProxy, can't run them if ROLE_DumbProxy)
Server > Replicate variables and notifications (functions server wants to force on clients) to client
Client > Call simulated functions, if simulated function was defined in replication block (Role<Role_Authority) that function will be called on server
.... > Clientside simulated functions won't be sent to server if it wasn't called from an actor Owned by the client (or the client himself)
What you'll most likely be using:
Weapon is spawned, player grabs it, it becoms part of the inventory chain.
Client asks to bring it up, server brings it up and tells client to do so as well.
Once the weapon is up, it becomes ready to fire (Idle state is an indicator of this)
Client PlayerPawn will call ClientFire(), it's server version calls Fire(), these are used to determine what kind of state we enter.
Event simulation, Tick, Timer, and PostRender will be called on clients (PostRender won't be called on dedicated server, i believe)
Use Tick for Animation, Sounds. In server version of Tick, also create the projectiles.
In Timer: make it return immediately on Clients; on Server let it continue for target scanning, save those in a list (don't use structs) that gets sent to the owner client.
Use PostRender to draw the data received from Server, in standalone games, the data is generated here so .
Warnings: Pointers to actors that haven't been replicated, won't be replicated, so passing the Pawn actor references won't work.
PlayerReplicationInfo is bAlwaysRelevant=true, those pointers are allowed.
My personal preference:
vector PLocation;
PlayerReplicationInfo ExtraInfo; (optional, for players and bots)
vector PVelocity; (because we want to predict the location changes between those 0.5-1.0 seconds intervals).
(use Tick to modify PLocation according to PVelocity to 'Simulate' heat signal location until server updates both location and velocity again, you may not want to limit this only to client Tick)
then: NumTargets so that PostRender don't attempt to draw all the 16 (or 32) replicated array.
Once you get the weapon to at least fake it's firing on clients, well discuss the projectile simulation.
- Voodoo Doll
- Average
- Posts: 42
- Joined: Wed Aug 03, 2011 2:27 am
- Location: Russian Federation
Re: Replication of weapon created from scratch: need help
OK, for now there are these changes:
all time management of animation/reloading, dodge is moved from timer() to tick();
in the timer() were kept pawn(owner).picktarget() and bpointing altering, as in SniperRifle code;
ClientAltFire is just disabled, ClieentFire plays anim of pawn, plays appropriate sound, sets mesh offset and time (i.e. moves 1st person mesh for further animation);
fire() moved inside Idle state;
Zooming state removed.
is this correct?
I have one issue with zoom, if bHoldZoom is false (or if altfire acts as zoom toggle), the code sets bownscrosshair to true and then immediately to false, so if zooming in condition stay first, i can't zoom out, or if it stay last, i can't zoom in. If I try to move zoom control to external function, thats cause crosshair blink at tick() rate, so I set bholdzoom to true. If there is no way to deal with this, it's need to remove bHoldZoom variable and let the rifle acts as this variable is always true, I think...
the code:
this is with one more feature, live camera feed from self-aiming projectiles. to let this working basic slow projectile class should be altered like this:
to enable this add bTrackerLiveFeed=True string to hercfg.ini
all time management of animation/reloading, dodge is moved from timer() to tick();
in the timer() were kept pawn(owner).picktarget() and bpointing altering, as in SniperRifle code;
ClientAltFire is just disabled, ClieentFire plays anim of pawn, plays appropriate sound, sets mesh offset and time (i.e. moves 1st person mesh for further animation);
fire() moved inside Idle state;
Zooming state removed.
is this correct?
I have one issue with zoom, if bHoldZoom is false (or if altfire acts as zoom toggle), the code sets bownscrosshair to true and then immediately to false, so if zooming in condition stay first, i can't zoom out, or if it stay last, i can't zoom in. If I try to move zoom control to external function, thats cause crosshair blink at tick() rate, so I set bholdzoom to true. If there is no way to deal with this, it's need to remove bHoldZoom variable and let the rifle acts as this variable is always true, I think...
the code:
Code: Select all
class herwpn expands TournamentWeapon config(hercfg);
var vector hitlocation,hitnormal, // hit effects calcs
starttrace,endtrace, // zoom/aim calcs
x,y,z; // draw/spawn offset calcs
var float ims, // initial mouse sensitivity of owner's pawn
radpitch, // zoom calcs
range, // range in m
ctime,ctime2, // delay timestamps
pmvo; // player mesh view offset
var int newfov, // zoomed FOV
ifov, // initial FOV of owner's pawn
mode, // default/current fire mode
xdir,ydir, // dodge direction
tlockdist, // maybe target lock distance
ldzr; // range to target/wall for auto zooming
var bool fired, // fired flag
forced, // dodged in water flag
cmod, // color mode (translucent/shaded)
tlock, // maybe lock flag for bot
tlfd; // target destroyed flag
var globalconfig string MapToggleColorModeKeyName,MapCycleFireModeKeyName,MapForceDodgeKeyName;
var globalconfig bool bAutoMapKeys,bDisableFaces,bDisableCompass,bHoldZoom,bForceNonFOVZoomCenter,bNonFOVZoom,bTrackerLiveFeed;
var globalconfig int DefaultMode; // these explained in manual
var fontinfo fi; // Botpack Arial font handle
var pawn tr; // tracked target
var herprja tlf,ltlf; // tracking projectile live feed
replication{ // here are referred most of class' functions and variables, but it's seems need to be improved...
reliable if(role>role_none) mode,hertogglecolormode,hercyclefiremode,hersavecfg,herforcedodge,performdodge,
maptogglecolormodekeyname,mapcyclefiremodekeyname,mapforcedodgekeyname,bAutoMapKeys,bDisableFaces,
bDisableCompass,bHoldZoom,regularfire;
}
simulated function postrender(canvas c){ // onscreen draw routine
local rotator r, // absolute rotator to target
s; // relative rotator to target
local float rdc, // pitch/yaw of rotator multiplier for lock sensing
d, // distance to target
scale; // textures scale
local bool lock, // target lock flag
lockdist, // rangefinder lock flag
dl; // range selector
local byte pt; // target team identifier
local int i, // counter
di, // scaled distance
xp,yp, // target label coords
il, // locked targets counter
ib[3], // locked targets flag
xp0,yp0; // basic render coords
local vector dir, // direction to target
a; // target location
Local playerpawn p; // owner handle
Local pawn t; // target handle
local texture lf[3]; // locked targets faces
local string lnn[3]; // locked targets names
local playerreplicationinfo pri; // target's replication info
p=playerpawn(owner); // acquire owner handle
if(p==none) return; // terminate if such is none
scale=c.clipx/640; // calculate textures scaler
// set basic coords
if(bnonfovzoom && !bforcenonfovzoomcenter){ // zoom via drawportal()
if(p.handedness==-1){ // right hand
xp0=c.clipx;
yp0=c.clipy;
xp0-=scale*256;
yp0-=scale*256;
xp0-=c.clipy/8;
yp0-=c.clipy/8;
}else if (p.handedness==1){ // left hand
yp0=c.clipy;
yp0-=scale*256;
xp0=c.clipy/5;
yp0-=c.clipy/8;
}else{ // center or none hand
xp0=c.clipx/2;
xp0-=scale*128;
yp0=c.clipy;
yp0-=scale*256;
yp0-=c.clipy/7;
}
}
if(!bnonfovzoom || bforcenonfovzoomcenter){ // zoom via FOV modifying or hand override
xp0=c.clipx/2;
xp0-=scale*126;
yp0=c.clipy/2;
yp0-=scale*125;
}
getaxes(p.viewrotation,x,y,z); // convert current view rotation to vectors
// draw zoomed area, if zooming via drawportal(). with FOV 2.5 times smaller than zoomed (to compensate vieving area for correct labels positioning):
if(bownscrosshair && bnonfovzoom) c.drawportal(xp0,yp0,scale*256,scale*256,p,p.location+x*2+z*(p.eyeheight-1),p.viewrotation,newfov/2.5,true);
if(btrackerlivefeed && (tlf!=none || ltlf!=none) && bownscrosshair){
if(tlf!=none && tlf.w==0){
c.Style=erenderstyle.sty_normal;
c.drawportal(xp0+scale*137,yp0+scale*137,scale*96,scale*96,tlf,tlf.location,tlf.rotation,60,true);
}
if(tlf!=none && tlf.w==0){
c.Style=erenderstyle.sty_translucent;
c.drawcolor.r=0;
c.drawcolor.g=32;
c.drawcolor.b=0;
}else{
c.drawcolor.r=32;
c.drawcolor.g=32;
c.drawcolor.b=32;
}
c.setpos(xp0+scale*136,yp0+scale*136);
c.drawicon(texture'static_a00',scale/2.65);
if(tlf!=none){
c.Style=erenderstyle.sty_normal;
if(fi!=none && c.clipx>=1024) c.font=fi.getbigfont(c.clipx); // attempt to select Botpack's big font
else c.font=c.smallfont; // otherwise, set regular small console font
c.setpos(xp0+scale*144,yp0+scale*144);
c.drawcolor.r=0;
c.drawcolor.g=255;
c.drawcolor.b=0;
if(tlf.tr!=none && tlf.tr.health<0) tlfd=true;
if(tlfd) c.drawtext("Target destroyed");
else{
if (tlf.tr!=none){
c.drawcolor.r=255;
c.drawcolor.g=255;
c.drawtext("Tracking...");
i=0; // reset counter
range=int(Vsize(tlf.tr.location-tlf.location)/200);
if(range<1) range=1; // restict values interval
if(range>30) range=30;
c.Style=erenderstyle.sty_translucent;
while(i<range){ // rangefinder bar draw cycle
c.setpos(xp0+(140*scale)+(i*3*scale),yp0+(228*scale)); // set current bar item position
hersetrangescalecolor(c,true,i);
c.drawicon(texture'herload',scale/4); // draw current rangefinder bar item
i++; // go next item
}
}
else{
c.drawcolor.r=255;
c.drawcolor.g=0;
c.drawtext("No target");
}
}
}
c.drawcolor.r=255;
c.drawcolor.g=255;
c.drawcolor.b=255;
}
c.bnosmooth=false; // disable canvas smoothing
if(!cmod) c.Style=erenderstyle.sty_translucent; else c.style=erenderstyle.sty_masked; // select color mode
dl=false; // reset zooming to wall
il=0; // reset target counter
while(il<3){ // nulling targets array
ib[il]=0;
lnn[il]="";
lf[il]=none;
il++;
}
il=0; // reset target counter after cycle
ldzr=0; // reset range througn wall
tr=none; // reset tracked target
foreach AllActors(class'Pawn',T){ // enumerate targets cycle
pt=playerpawn(owner).playerreplicationinfo.team+1; // set target team to not same as owner team
if(t.isa('playerpawn')){ // attempt to detect team of player, controlling playerpawn
pri=playerpawn(t).playerreplicationinfo; // acquire PRI handle
pt=pri.team; // acquire team
if(level.game.isa('tournamentgameinfo')){ // check whether gametype is not singleplayer
if(pri.talktexture!=none) lf[il]=pri.talktexture; // acquire face
lnn[il]=pri.playername; // acquire name
ib[il]=1; // set this target flag
}
} // team detection of playerpawn controlling player ends
if(t.isa('bot')){ // attempt to detect team of bot
pri=bot(t).playerreplicationinfo; // acquire PRI handle
pt=pri.team; // acquire team
if(level.game.isa('tournamentgameinfo')){ // check whether gametype is not singleplayer
if(pri.talktexture!=none) lf[il]=pri.talktexture; // acquire face
lnn[il]=pri.playername; // acquire name
ib[il]=1; // set this target flag
}
} // team detection of bot ends
// chech whether the target is not owner, firefiles or such, and not player of same team as owner (NOTE: stationary pawns are included in this condition):
if(t!=owner && pt!=playerpawn(owner).playerreplicationinfo.team && !t.isa('flockpawn') && !t.isa('flockmasterpawn')){
a=t.location; // acquire target location
a.z-=24; // modify Z axis for label
dir=a-p.location; // calculate direction
d=vsize(dir); // calculate distance
dir/=d; // scaling direction vector to (-1;-1;-1)-(1;1;1) space for correct labels positioning
if((dir dot (p.defaultfov/p.desiredfov*x))>0.7){ // check whether the label should be visible, according to current FOV
r=rotator(dir); // calculate rotator from direction
s=p.viewrotation-r; // calculare relative rotator
while(s.yaw<65536) s.yaw+=65536; // subtracting full 360 dgr. rotations from relative rotator
while(s.yaw>65536) s.yaw-=65536;
if(s.pitch<65536) s.pitch+=65536;
if(s.pitch>65536) s.pitch-=65536;
if(s.pitch>32768) s.pitch-=65536; // subtract until -180..+180 dgr. rotation
if(s.yaw>32768) s.yaw-=65536;
if(s.pitch<0) s.pitch*=-1; // calculate positive values
if(s.yaw<0) s.yaw*=-1;
if(t.health<0){ // check whether target is dead
lock=false; // reset lock flags
lockdist=false;
c.drawcolor.r=255; // set red label color
c.drawcolor.g=0;
c.drawcolor.b=0;
}else{ // otherwise set color depending on distance, shown in manual
if(d>1024){
d-=1024;
d/=4096;
if(d<0) d=0;
if(d>1) d=1;
c.drawcolor.r=0;
c.drawcolor.g=255-(225*d);
c.drawcolor.b=0;
}else{
d-=256;
d/=768;
if(d<0) d=0;
if(d>1) d=1;
if(d>0.5) c.drawcolor.r=255-255*d; else c.drawcolor.r=64+192*d;
c.drawcolor.g=64+192*d;
c.drawcolor.b=255;
}
d=vsize(t.location-p.location); // calculate distance because d was altered
if(d<500) rdc=500; // scaling max. relative rotation depending on distance
if(d>=500) rdc=420;
if(d>=1000) rdc=350;
if(d>=2500) rdc=130;
if(d>=4000) rdc=100;
if(d>=5000) rdc=80;
if(d>=6000) rdc=70;
if(d>=7000) rdc=65;
lock=true; // set lock flags
lockdist=true;
rdc/=(42/t.collisionradius); // scaling to radius of pawn
if(s.yaw>rdc || s.pitch>rdc) lock=false; // if rotation exceeds maximum, clear lock flag
rdc*=(42/t.collisionradius)*1.5; // scaling 1.5 times bigger for rangefinder
if(s.yaw>rdc || s.pitch>rdc) lockdist=false; // if rotation exceeds maximum, clear rangefinder lock flag
} // label color devnition ends
xp=(c.clipx/2)+((dir dot y))*((c.clipx/2)/tan(newfov*pi/360))/(dir dot x); // calculate label position
yp=(c.clipy/2)+(-(dir dot z))*((c.clipx/2)/tan(newfov*pi/360))/(dir dot x);
if(bnonfovzoom && bownscrosshair){ // modify position to fit in zoomed window, if zooming via drawportal()
xp-=c.clipx/2;
xp+=(scale*128)+xp0;
yp-=c.clipy/2;
yp+=(scale*128)+yp0;
}
// if the position is in the zoomed window or if not zooming, draw the label
if((xp>=xp0 && xp<=xp0+(scale*256) && yp>=yp0 && yp<=yp0+(scale*256)) || !bownscrosshair){
c.setpos(xp-(7*scale),yp-(7*scale));
c.drawicon(texture'herdot',scale);
}
if(lock){ // check whether aim lock on target
c.style=erenderstyle.sty_normal; // set lock sign color, in translucent mode
if(!cmod){
c.drawcolor.r=64;
c.drawcolor.g=64;
c.drawcolor.b=255;
}else{ // or in shaded mode
c.drawcolor.r=0;
c.drawcolor.g=0;
c.drawcolor.b=192;
}
c.setpos(xp-(15*scale),yp-(15*scale)); // set sign position and draw it
c.drawicon(texture'herlock',scale);
if(!cmod) c.style=erenderstyle.sty_translucent;
else c.style=erenderstyle.sty_masked;
if(mode==3 || d<tlockdist) tlock=true;
} // target lock check ends
if(lockdist){ // check whether rangefinder lock in target
tr=t; // assign current tracking target
di=int(vsize(t.location-p.location)/200); // scale distance to target to rangefinder units (200 UU, or 4.5 M)
ldzr=di*200; // restore as integer, zoom through wall in necessary FOV, if nonzero
if(di<1) di=1; // restrict values interval
if(di>30) di=30;
dl=true; // set range selector to target distance priority
if(ib[il]==1 && !bDisableFaces){ // face and name ot current target were set, check whether we need to display them
c.style=erenderstyle.sty_normal; // set normal render style, white color (for correct texture palette scaling)
c.drawcolor.r=255;
c.drawcolor.g=255;
c.drawcolor.b=255;
if(il<3){ // check whether target counter doesn't exceed 3 (display 3 max)
if(lf[il]!=none){ // check whether face texture is not null
c.setpos(10,c.clipy-(160*scale)-(88*il*scale)); // set position
c.drawicon(lf[il],scale); // display face
}
if(lnn[il]!=""){ // check whether name is not null
c.setpos(10,c.clipy-(94*scale)-(88*il*scale)); // set position
if(fi!=none && c.clipx>=1024) c.font=fi.getbigfont(c.clipx); // attempt to select Botpack's big font
else c.font=c.smallfont; // otherwise, set regular small console font
if(pt==0){ // targer is on red team
c.drawcolor.g=0;
c.drawcolor.b=0;
}
if(pt==1){ // target is on blue team
c.drawcolor.r=0;
c.drawcolor.g=0;
}
if(pt==2){ // target is on green team
c.drawcolor.r=0;
c.drawcolor.b=0;
}
if(pt==3) c.drawcolor.b=0; // target is on gold team
c.drawtext(lnn[il]); // display name
} // name check ends
} // target qty check ends
} // face/name of target diaplaying ends
il++; // go next array item
} // rangefinder lock check ends
bpointing=lock; // assign aiming variable to same as target lock flag, for bot
// in theory, this allows bots sensing aiming through walls,
// since comparing relative rotation to all pawns, or even to
// radiusactors() pawns eats CPU too much
} // check whether target label is visible ends
} // check the pawn as necessary target ends
if(!cmod) c.style=erenderstyle.sty_translucent; // restore draw mode, for crosshair elements
else c.style=erenderstyle.sty_masked;
} // targets enumeration cycle ends
if(!dl) di=0; // if no target sensed by rangefinder, select hit distance instead of target distance
if (bOwnsCrosshair) { // check whether owner is zooming
c.setpos(xp0,yp0); // set initial coords
if(!cmod){ // select draw mode
c.drawcolor.r=25;
c.drawcolor.g=245;
c.drawcolor.b=25;
}else{
c.drawcolor.r=0;
c.drawcolor.g=64;
c.drawcolor.b=0;
}
c.drawicon(texture'hercrossa',scale); // draw first crosshair layer
c.setpos(xp0,yp0);
if(!cmod) c.drawcolor.r=245; // modify color for selected draw mode
else c.drawcolor.r=64;
c.drawicon(texture'hercrossb',scale); // draw second layer
starttrace=p.location; // set start range calcs location
adjustedaim=pawn(owner).adjustaim(1000000,starttrace,2.75*aimerror,false,false); // adjust owner's aim
endtrace=starttrace+(10000*vector(adjustedaim)); // calculate endpoint of aimed vector
pawn(owner).traceshot(hitlocation,hitnormal,endtrace,starttrace); // trace shot
c.setpos(xp0-(12*scale),yp0+(260*scale)); // set "range" word position
if(!cmod) c.drawcolor.r=25; // modify color for selected draw mode
else c.drawcolor.r=0;
c.drawicon(texture'herrng',scale); // draw
c.setpos(xp0+(238*scale),yp0+(239*scale)); // set fired/rangefinder lock sign position
c.drawcolor.r=255;
if(!fired){ // check whether weapon is reloading
c.drawcolor.g=5; // set red color
c.drawcolor.b=5;
c.drawicon(texture'herload',scale); // draw the sign
}else if(di>0){ // check for rangefinder lock
c.drawcolor.g=255; // set yellow color
c.drawcolor.b=5;
c.drawicon(texture'herload',scale); // draw the sign
}
if(di>0) range=di; // if range selector set to target distance, assign that
else{ // otherwise, assign distance to hit point
range=int(Vsize(StartTrace-HitLocation)/200);
if(range<1) range=1; // restict values interval
if(range>30) range=30;
}
c.drawcolor.b=0; // set initial color
i=0; // reset counter
while(i<range){ // rangefinder bar draw cycle
c.setpos(xp0+(28*scale)+(i*8*scale),yp0+(258*scale)); // set current bar item position
hersetrangescalecolor(c,false,i);
c.drawicon(texture'herload',scale); // draw current rangefinder bar item
i++; // go next item
} // ragefinder bar draw cycle ends
if(!bdisablecompass){ // check whether we need to draw compasses
s=p.viewrotation; // calculate absolute owner rotation
while(s.yaw<65536) s.yaw+=65536; // subtract full 360 dgr. rotations
while(s.yaw>65536) s.yaw-=65536;
if(s.pitch<65536) s.pitch+=65536; // set rotation to -32768..32767 interval
if(s.pitch>65536) s.pitch-=65536;
if(s.pitch>32768) s.pitch-=65536;
if(s.yaw>32768) s.yaw-=65536;
c.drawcolor.r=245; // set red color
c.drawcolor.g=5;
c.drawcolor.b=5;
xp=xp0+235*scale; // assign initial position
yp=yp0+120*scale;
if(s.pitch<0){ // modify position if owner looks down, scale width is 16384
d=-s.pitch;
d=d/16384*102;
yp+=int(d*scale);
}else{ // modify position if owner looks up, scale width is 18000
d=s.pitch;
d=d/18000*102;
yp-=int(d*scale);
}
c.setpos(xp,yp); // set position
c.drawicon(texture'herhorzcomp',scale); // draw vertical compass
xp=xp0+120*scale; // assign initial position
yp=yp0+236*scale;
d=s.yaw; // modify position
d=d/32768*102;
xp+=int(d*scale);
c.setpos(xp,yp); // set position
c.drawicon(texture'hervertcomp',scale); // draw horizontal compass
} // compass displaying ends
} // crosshair displaying ends
c.Style=ERenderStyle.STY_Normal; // restore normal draw style, white color, for proper rendering of other classes
c.drawcolor.r=255;
c.drawcolor.g=255;
c.drawcolor.b=255;
} // onscreen drawing routine ends
simulated function TweenDown(){ // zoom out routine
bOwnsCrosshair=false; // disable crosshair rendering
playerviewoffset.x=pmvo;
if(playerpawn(owner)!=none){ // chech whether owner exists
newfov=ifov; // set initial FOV for correct target labels positioning
PlayerPawn(Owner).desiredFOV=ifov; // instant restore FOV
PlayerPawn(Owner).FOVangle=ifov;
playerpawn(owner).mousesensitivity=ims; // restore mouse sensitivity
owner.playsound(sound'herzoomout',,96); // play zoom out sound
}
}
simulated function float RateSelf(out int busealtmode){ // rateself() - original from Botpack.SniperRifle with altered coefficients
local float d;
local pawn p;
local bot b;
p=pawn(owner);
b=bot(owner);
if(p==none) return 0;
if(AmmoType==None) giveammo(p);
if(AmmoType.AmmoAmount<=0) return -2;
bUseAltMode=0;
if(b!=none && b.bSniping) return AIRating+2.3;
if(p.enemy!=none){
d=vsize(p.enemy.location-owner.location);
if(mode==3){
if(d>1280){
if(d>2560) return (AIRating+1.5);
return (AIRating+FMin(0.0002*d,0.9));
}
}else{
if(d<2560){
if(d<1024) return (AIRating+1.5);
return (AIRating+FMin(1/d,1.0));
}
}
}
return AIRating;
}
simulated function setHand(float Hand){ // set 1st person mesh postion depending on hand (default is 0, for center/none hand)
if(hand==1) playerviewoffset.y=-200;
if(hand==-1) playerviewoffset.y=200;
}
simulated function PlayFiring(){} // disable firing animation
simulated function Timer(){ // time features
local actor targ; // target
local vector FireDir; // original UT engine variables
local float bestAim, bestDist;
local pawn p; // owner
P=Pawn(Owner); // acquire owner, terminate if none
if(P==None){
GotoState('');
return;
}
firedir=vector(p.viewrotation); // from UT SniperRifle code
bestaim=0.95;
targ=p.picktarget(bestaim,bestDist,firedir,owner.location);
if(Pawn(targ)!=none || tlock) bpointing=true; // set pointing flag if set target lock flag
else if (p.bfire==0 && p.baltfire==0) bPointing=false;
if (AmmoType==None) GiveAmmo(Pawn(Owner)); // check ammo
if(AmmoType.AmmoAmount<=0){ // if no ammo, prevent zooming - is it need to override this restriction?
if(bownscrosshair) tweendown();
gotostate('idle');
}
if(owner.isa('bot') && targ!=none){ // select preferred fire mode
if(vsize(owner.location-targ.location)>1024 && ammotype.ammoamount>=40) mode=3;
else mode=2;
}
settimer(1.0,true); // set next timer() call
}
simulated function tick(float f){ // critical time features
local pawn p; // owner
p=pawn(Owner); // acquire owner, terminate if none
if(p==none) return;
if(pmvo<1000){
pmvo+=63;
if (!bownscrosshair) playerviewoffset.x=pmvo; // simulate reload mesh animation if necessary
else playerviewoffset.x=-2000; // hide in zoom mode
}
if(P.bFire!=0 && mode==2) Fire(0.0); // if in regular autofire mode, execute fire()
if(p.baltfire!=0 && !bownscrosshair){ // if altfire button is holding, then zoom in
bOwnsCrosshair=true; // enable crosshair rendering
playerviewoffset.x=-2000;
tracefire(0);
if(ldzr==0) range=Vsize(StartTrace-HitLocation); // if range selector is on hit distance, assign it,
else range=ldzr; // otherwise, assign target distance
newFOV=90/(range/300); // calculate zoomed FOV
if (newFOV>90) newFOV=90; // restrict max FOV to 90 dgr.
if(bnonfovzoom){ // check whether using zooming via drawportal()
if (newFOV<12.5) newFOV=12.5; // restrict min FOV to 5 dgr. (12.5/2.5)
playerpawn(owner).mousesensitivity=ims/(90/newfov); // modify owner's playerpawn mouse sensitivity according to zoom level
}else{ // using zooming via FOV altering
PlayerPawn(Owner).desiredFOV=newFOV; // instant set FOV
PlayerPawn(Owner).FOVangle=newFOV;
}
owner.playsound(sound'herzoomin',,96); // play zoom in sound
return;
}
bholdzoom=true; // override zoom toggle to hold
if((p.baltfire==0 && bownscrosshair && bHoldZoom)){ // if altfire button isn't
tweendown(); // holding, or if pressed while
GotoState('Idle'); // zoomed, then zoom out
}
if(level.timeseconds-ctime2>=0.15){ // update dodge in water timestamp, and restore speed in water
forced=true;
p.waterspeed=200;
}
if(fired) return; // terminate if reloading
if(mode<3){ // reload at 350 RPM rate in tracking target, regular single/auto fire mode
if(level.timeseconds-ctime<0.171) return;
fired=true;
}else{ // reload at 250 RPM rate in increased power fire mode
if(level.timeseconds-ctime<0.24) return;
fired=true;
}
if(fired) owner.playsound(sound'herloaded',,32); // if reloaded, play sound
}
state Idle{ // main state
simulated function bool ClientAltFire(float f){
return false;
}
simulated function bool ClientFire(float f){
if(mode<3) pmvo=-77; // set initiial pseudoanimation mesh position
else pmvo=-512; // increased power fire mode
fired=false; // set flag
ctime=level.timeseconds; // update timer
pawn(Owner).PlayRecoil(1); // play pawn fire animation
if(mode!=3) owner.playsound(sound'herfire1',,32); // play regular fire sound
else owner.playsound(sound'herfire2',,32); // play increased power fire sound
if (playerpawn(owner)!=none) playerpawn(Owner).ShakeView(0.1,200,4); // shake owner view
return true;
}
simulated function AltFire(float f){}
simulated function Fire(float f){ // global fire routine
local vector ol; // owner location
local herprja h; // projectila handle
local pawn p; // owner
p=pawn(owner); // acquire owner, terminate if none
if(p==none) return;
if(fired){ // check whether is not reloading
if (AmmoType==None) GiveAmmo(Pawn(Owner)); // check ammo
if (AmmoType.UseAmmo(1)){ // take 1 ammo unit
TraceFire(0.0); // calculate adjustedaim
ol=owner.location; // set initial projectile spawn location
ol.z+=pawn(owner).eyeheight;
ol.z-=1;
if(mode==4){ // atempt to fire in tracking target mode
if (AmmoType.UseAmmo(4)){ // take 5 ammo in all
h=spawn(class'herprja',,,ol,adjustedaim); // attempt to spawn projectile
if(h!=none){ // attempt to find projectile on level
tlf=h;
ltlf=h;
tlfd=false;
if(tr!=none) h.tr=tr; // set current tracked target, if any
else p.clientmessage("No target.");
}
if (playerpawn(owner)!=none) playerpawn(Owner).ShakeView(0.1,300,5); // shake view
}else{ // insufficient ammo, going regular fire
p.clientmessage("Insufficient core energy - firing with regular force.");
mode=1; // set mode
regularfire(ol); // execute
}
} // traacking target fire ends
if(mode==3){ // attempt to fire in increased power mode
if (AmmoType.UseAmmo(19)){ // take 20 ammo in all
spawn(class'herprjb',,,ol,adjustedaim); // spawn unhandled projectile
if (playerpawn(owner)!=none) playerpawn(Owner).ShakeView(0.1,400,8); // shake view
}else{ // insufficient ammo, going regular fire
p.clientmessage("Insufficient core energy - firing with regular force.");
mode=1; // set mode
regularfire(ol); // execute
}
} // increased power mode fire ends
if(mode==1 || mode==2) regularfire(ol); // execute fire in regular mode, single/auto
clientfire(f);
tlock=false; // reset bot target lock flag
} // fire attempt ends
} // reloading check ends
} // global fire routine ends
simulated function BeginState(){ // entry point
bPointing=false; // reset bot aim flag
SetTimer(1.0,false); // execute timer()
Super.BeginState();
}
simulated function EndState(){ // end point
SetTimer(1.0,false); // execute timer()
Super.EndState();
}
Begin: // state initialization
bPointing=False; // reset bot aim flag
sleep(0.5); // initial delay
if (AmmoType==None) GiveAmmo(Pawn(Owner)); // check ammo
if(ammotype!=none && ammotype.ammoamount<=0 && pawn(owner).weapon==self) Pawn(Owner).SwitchToBestWeapon(); // suggest best weapon if no ammo
if(Pawn(Owner).bFire!=0) Fire(0.0); // if fire button holding, then fire
Disable('AnimEnd'); // maybe this can be removed
}
simulated function PlayIdleAnim(){} // disable idle animation
simulated function tweentostill(){} // disable still animation
simulated function postbeginplay(){ // setup
local bool wcfg; // write config flag
fired=true; // flags initialization
forced=true;
cmod=false;
wcfg=false;
newfov=90;
tlfd=false;
ltlf=none;
tlf=none;
if(MapToggleColorModeKeyName==""){ // set draw style switch key, if none
MapToggleColorModeKeyName="R";
wcfg=true; // set write flag
}
if(MapCycleFireModeKeyName==""){ // set fire mode switch key, if none
MapCycleFireModeKeyName="F";
wcfg=true; // set write flag
}
if(MapForceDodgeKeyName==""){ // set force dodge key, if none
MapForceDodgeKeyName="Alt";
wcfg=true; // set write flag
}
if(DefaultMode==0){ // set regular single fire mode (0 means undefined)
DefaultMode=1;
wcfg=true; // set write flag
}
mode=defaultmode; // set default mode
if(wcfg){ // if we need to save configuration, then do it
bautomapkeys=true; // some default options
bHoldZoom=true;
bNonFOVZoom=False;
bForceNonFOVZoomCenter=True;
bDisableFaces=False;
bDisableCompass=False;
saveconfig(); // store
}
fi=fontinfo(spawn(class<actor>(dynamicloadobject("botpack.fontinfo", class'Class')))); // create fontinfo
tlockdist=1280; // set default lock distance
}
simulated function destroyed(){ // destroying
super.destroyed(); // call parent routine
if(fi!=none) fi.destroy(); // destroy fontinfo
}
simulated function playselect(){ // setup on select
local playerpawn p; // owner
p=playerpawn(owner); // acquire owner
ifov=90; // initial FOV, undefined
ims=1.0; // initial mouse sensitivity, undefined
if(p!=none && bautomapkeys){
ifov=p.fovangle; // initial FOV, from owner
ims=p.mousesensitivity; // initial mouse sensitivity, from owner
p.consolecommand("set input "$maptogglecolormodekeyname$" hertogglecolormode"); // set input keymacros
p.consolecommand("set input "$mapcyclefiremodekeyname$" hercyclefiremode");
p.consolecommand("set input "$mapforcedodgekeyname$" herforcedodge");
}
}
exec function hertogglecolormode(){ // switch draw style
cmod=!cmod;
}
exec function hercyclefiremode(){ // switch fire mode
local pawn p; // owner
p=pawn(owner); // acquire owner and terminate if none
if(p==none) return;
if(ammotype==none) giveammo(p); // check ammo
if(mode==1){ // change to regular auto mode
p.clientmessage("Fire mode: Auto (regular)",'Pickup');
mode=2;
return;
}
if(mode==2){ // change to increased power mode
if(ammotype.ammoamount>=20){ // check ammo qty
p.clientmessage("Fire mode: Increased power",'Pickup');
mode=3;
}else{
p.clientmessage("Insufficient core energy.");
if(ammotype.ammoamount>=5){ // check ammo qty
p.clientmessage("Fire mode: Tracking target",'Pickup');
mode=4;
}else{
p.clientmessage("Fire mode: Single (regular)",'Pickup');
mode=1;
}
}
return;
}
if(mode==3){ // change to tracking mode
if(ammotype.ammoamount>=5){ // check ammo qty
p.clientmessage("Fire mode: Tracking target",'Pickup');
mode=4;
}else{
p.clientmessage("Insufficient core energy.");
p.clientmessage("Fire mode: Single (regular)",'Pickup');
mode=1;
}
return;
}
if(mode==4){ // change to regular single mode
p.clientmessage("Fire mode: Single (regular)",'Pickup');
mode=1;
return;
}
}
exec function herforcedodge(){ // force dodge
local playerpawn p; // owner
p=playerpawn(owner); // acquire owner, terminate if none
if(p==none) return;
xdir=0; // reset directions
ydir=0;
if(p.bwasforward) xdir=1; // assign directions
if(p.bwasback) xdir=-1;
if(p.bwasleft) ydir=1;
if(p.bwasright) ydir=-1;
performdodge(p); // execute dodge movement
}
exec function hersavecfg(){ // ingame configuration store
DefaultMode=mode;
saveconfig();
if(pawn(owner)!=none) pawn(owner).clientmessage("H.E.R. configuration saved.",'Pickup');
}
simulated function performdodge(playerpawn p){ // dodge movement routine
local rotator r;
if(p.Physics==PHYS_Walking && (xdir!=0 || ydir!=0)){ // dodge in air, to any side
GetAxes(p.Rotation,X,Y,Z); // convert rotation to vectors
p.Velocity=(xdir*1.5*p.GroundSpeed+p.Velocity Dot X)*X+(ydir*1.5*p.GroundSpeed+p.Velocity Dot Y)*Y; // calculate dedge speed. 2 times faster than usual dodge
p.Velocity.Z=160; // restict vertical speed
p.PlayOwnedSound(p.JumpSound,SLOT_Talk,1.0,true,800,1.0); // play dodge sound
if(xdir==0){ // play necessary dodge animation(s)
if(ydir==1) p.PlayDodge(DODGE_Left);
if(ydir==-1) p.PlayDodge(DODGE_Right);
}else{
if(xdir==-1) p.PlayDodge(DODGE_Back);
if(xdir==1) p.PlayDodge(DODGE_Forward);
}
p.SetPhysics(PHYS_Falling); // set dodge physics
}else if(p.Physics==PHYS_Swimming){ // dodge in water, in front only
if(forced){ // time restriction
p.waterspeed=45000; // refedine water speed
p.Velocity=vector(p.viewrotation)*2000; // add speed
r=p.viewrotation;
if(r.pitch<65536) r.pitch+=65536; // restrict vertical speed
if(r.pitch>65536) r.pitch-=65536;
if(r.pitch>32768) r.pitch-=65536;
if(r.pitch>3000) p.Velocity.Z=640;
forced=false; // set restriction flag
ctime2=level.timeseconds; // update timer
}
}
} // dodge movement routine ends
simulated function TraceFire(float acc){ // adjustedaim vector calcs
GetAxes(Pawn(owner).ViewRotation,X,Y,Z); // convert view rotation to vectors
if ((Pawn(Owner).ViewRotation.Pitch>=0) && (Pawn(Owner).ViewRotation.Pitch<=18000)) // pawn looks up, scale width is 18000
radpitch=float(Pawn(Owner).ViewRotation.Pitch)/float(182)*(Pi/float(180));
else radpitch=float(Pawn(Owner).ViewRotation.Pitch-65535)/float(182)*(Pi/float(180)); // pawn looks down, scale width is 16384
StartTrace=Owner.Location+Pawn(Owner).EyeHeight*Z*cos(radpitch); // start trace location
EndTrace=StartTrace+(10000*vector(AdjustedAim)); // calculate end trace
AdjustedAim=pawn(owner).AdjustAim(1000000,StartTrace,2.75*AimError,False,False); // adjust aim
Pawn(Owner).TraceShot(HitLocation,HitNormal,EndTrace,StartTrace); // trace shot location
}
simulated function regularfire(vector ol){ // regular single fire
spawn(class'herprjar',,,ol,adjustedaim); // spawn projectile class
}
simulated function hersetrangescalecolor(canvas c,bool inv,int i){
if(!inv){
if(i>28){
c.drawcolor.r=65; // set red color, last item
c.drawcolor.g=0;
}else{
if(i>26){
c.drawcolor.r=65; // set orange color, items 27-29
c.drawcolor.g=29;
}else{
if(i>23){
c.drawcolor.r=65; // set more yellow color, items 24-26
c.drawcolor.g=46;
}else{
if(i>17){
c.drawcolor.r=65; // set yellow color, items 19-24
c.drawcolor.g=65;
}else{
if(i>9){
c.drawcolor.r=53; // set some yellow color, items 10-18
c.drawcolor.g=65;
}else{
c.drawcolor.r=0; // set green color, first 10 items
c.drawcolor.g=65;
}
}
}
}
}
}else{ // in reverse order
if(i>28){
c.drawcolor.r=0;
c.drawcolor.g=130;
}else{
if(i>26){
c.drawcolor.r=106;
c.drawcolor.g=130;
}else{
if(i>23){
c.drawcolor.r=130;
c.drawcolor.g=130;
}else{
if(i>17){
c.drawcolor.r=130;
c.drawcolor.g=92;
}else{
if(i>9){
c.drawcolor.r=130;
c.drawcolor.g=58;
}else{
c.drawcolor.r=130;
c.drawcolor.g=0;
}
}
}
}
} // color set conditions ends
}
}
Code: Select all
class herprja expands Projectile;
var vector pl, // previous location
id; // initial direction
var int ip, // child number
w; // child wait amount
var float ps,mv; // effects and tracking calcs
var pawn tr; // tracking target handle
function postbeginplay(){ // setup
w=0;
ip=0;
pl=location;
settimer(0.01,false);
}
function timer(){
local vector sd;
if(region.zone.bwaterzone){ // spawn bubbles each 32 UU in water
if(vsize(pl-location)>32){
pl=location;
spawn(class'herbubble');
}
}else{ // spawn trails each 128 UU in air
if(vsize(pl-location)>128){
pl=location;
spawn(class'hertrla');
}
}
if(tr!=none && tr!=instigator){ // track the target
if(tr.health>0){
sd=normal(tr.location-location);
if((sd Dot id)>0){
mv=vsize(velocity);
velocity=mv*normal(sd*mv+velocity);
setrotation(rotator(velocity));
}
}else tr=none;
}
settimer(0.01,true); // set next timer() call
}
auto state Flying{
simulated function ProcessTouch (Actor Other, Vector HitLocation){ // hurt the target
if (other!=instigator){
herflesheffects(other,hitlocation);
if(other==tr && instigator!=none && herwpn(instigator.weapon)!=none){
herwpn(instigator.weapon).tlfd=true;
tr=none;
}
Other.TakeDamage(500000,instigator,HitLocation,30000*Normal(Velocity),'exploded');
if(other.isa('projectile')) projectile(other).explode(other.location,normal(other.location));
}
}
simulated function Explode(vector HitLocation,vector HitNormal){ // explode on wall hit
local effects s;
local int j,xm;
local herprja m;
local vector x,y,z;
herplayexplodesound(); // play sound
herspawnsparks(HitLocation,HitNormal); // spawn sparks
if(ip<20){ // create child(s) advanced each 64 UU by X axis, 20 max
ip+=1;
GetAxes(Rotation,X,Y,Z);
j=0;
xm=0;
if(instigator!=none && herwpn(instigator.weapon)!=none && herwpn(instigator.weapon).tlf==self){
herwpn(instigator.weapon).tlf=none;
herwpn(instigator.weapon).ltlf=self;
}
while(j<64){
xm+=1;
j+=1;
m=spawn(class'herprja',,,HitLocation+HitNormal+x*64*xm); // attemp to spawn
if(m!=none){ // spawned successfully
if(instigator!=none && herwpn(instigator.weapon)!=none &&
herwpn(instigator.weapon).tlf==none && herwpn(instigator.weapon).ltlf==self){
herwpn(instigator.weapon).ltlf=none;
herwpn(instigator.weapon).tlf=m;
}
spawn(class'herprjard',,,HitLocation+HitNormal+x*64*xm); // spawn backward moving copy at same location to hit target strictly by wall
m.w=j; // set child parameters
m.ip=ip;
if(tr!=none) m.tr=tr; // set child target handle. if any
j=65; // halt the cycle
}else if(j>=64 && instigator!=none && herwpn(instigator.weapon)!=none
&& herwpn(instigator.weapon).ltlf==self) herwpn(instigator.weapon).ltlf=none;
}
}
destroy();
}
function BeginState(){
id=vector(rotation);
settimer(0.1,true);
}
Begin:
mesh=none; // hide mesh and wait, if need (if moved through massive wall, e. g. 1536 thickness, the child
Sleep(w*(0.006/level.timedilation)); // will be created instantly. To compensate child movement time we have to delay it.)
w=0;
if(!region.zone.bwaterzone){ // spawn trail in air
if(instigator==none) spawn(class'hertrla');
else if(vsize(instigator.location-location)>128) spawn(class'hertrla');
}else{ // or spawn bubble in water
if(instigator==none) spawn(class'herbubble');
else if(vsize(instigator.location-location)>64) spawn(class'herbubble');
}
Velocity=Vector(Rotation)*2000; // set initial speed
mesh=default.mesh; // restore mesh
}
simulated function HerPlayExplodeSound(){ // wall hit sounds
ps=frand();
if(ps>0.833) playsound(sound'herwall',,16,,300);
else{
if(ps>0.667) playsound(sound'herwall1',,16,,300);
else{
if(ps>0.5) playsound(sound'herwall2',,16,,300);
else{
if(ps>0.333) playsound(sound'herwall3',,16,,300);
else{
if(ps>0.167) playsound(sound'herwall4',,16,,300);
else playsound(sound'herwall5',,16,,300);
}
}
}
}
MakeNoise(1.0);
}
simulated function HerSpawnSparks(vector hl,vector hn){ // sparks
local effects s;
if(!region.zone.bwaterzone){
s=spawn(class'smallspark2',,,hl+hn*5,rotator(hn*2+VRand()));
if(s!=none) s.RemoteRole=ROLE_None;
if(rotator(hn).pitch<=0){
s=spawn(class'ut_sparks',,,hl+hn*5,rotator(hn*2+VRand()));
if(s!=none){
s.drawscale/=2;
s.RemoteRole=ROLE_None;
}
}
}
spawn(class'hertrlc',,,hl,rotator(hn));
}
simulated function HerFleshEffects(actor o,vector hl){ // flesh, blood and such
local effects e;
local vector v;
local int k;
if(o.bispawn){
o.playsound(sound'herflesh',,16,,300);
e=spawn(class'UT_BigBloodHit',,,hl);
e.drawscale=0.02;
for(k=0;k<(3);k++){
v=hl;
v.x+=5*FRand();
v.x-=7*FRand();
v.y+=5*FRand();
v.y-=7*FRand();
v.z+=5*FRand();
v.z-=7*FRand();
spawn(class'BloodBurst',,,v);
}
}else{
ps=frand();
if(ps>0.5) playsound(sound'herstuff',,16,,300);
o.playsound(sound'herstuff2',,16,,300);
}
}
Re: Replication of weapon created from scratch: need help
While I read your code, this might give you an idea on how to make switches/events/timers without actually using any kind of state code or timers().
Code: Select all
var bool bWasPressed;
var bool bTimer1Loop;
var float Timer1, Timer1Rate;
event Tick( float Delta)
{
if ( Timer1 > 0)
{
Timer1 -= Delta;
if ( Timer1 <= 0)
{
Timer1(); //Call timer
if ( bTimer1Loop )
Timer1 += Timer1Rate;
else
Timer1Rate = 0;
}
}
//Check if we just pressed altfire
if ( !bWasPressed && (POwner.bAltFire > 0) )
{
NotifyPress(); //Do something here right after we press a button
}
else if ( bWasPressed && (POwner.bAltFire == 0) )
{
NotifyRelease(); //Button was released
}
if ( POwner.bAltfire > 0 )
{
NotifyHold(); //Button is being held, add conditional bWasPressed if you want the Press and Hold notifies not to execute in the same tick
}
bWasPressed = POwner.bAltFire > 0; //Setting this for next tick
}