Replication of weapon created from scratch: need help

Discussions about Coding and Scripting
Post Reply
User avatar
Voodoo Doll
Average
Posts: 42
Joined: Wed Aug 03, 2011 2:27 am
Location: Russian Federation

Replication of weapon created from scratch: need help

Post by Voodoo Doll »

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.
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.
iloveut99
Skilled
Posts: 231
Joined: Mon Aug 16, 2010 10:25 pm

Re: Replication of weapon created from scratch: need help

Post by iloveut99 »

Pretty fun to play offline. An online version would be a must.
UT99.org

Re: Replication of weapon created from scratch: need help

Post by UT99.org »

medor wrote::thuup:

no work hidden weapond with it ?
Higor
Godlike
Posts: 1866
Joined: Sun Mar 04, 2012 6:47 pm

Re: Replication of weapon created from scratch: need help

Post by Higor »

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.
UT99.org

Re: Replication of weapon created from scratch: need help

Post by UT99.org »

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. :ironic:
Higor
Godlike
Posts: 1866
Joined: Sun Mar 04, 2012 6:47 pm

Re: Replication of weapon created from scratch: need help

Post by Higor »

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).
UT99.org

Re: Replication of weapon created from scratch: need help

Post by UT99.org »

medor wrote:May be help you i have videos tuto for weapons http://unrealtournament.99.free.fr/utfi ... ls-Weapon/
User avatar
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

Post by Voodoo Doll »

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.
Higor
Godlike
Posts: 1866
Joined: Sun Mar 04, 2012 6:47 pm

Re: Replication of weapon created from scratch: need help

Post by Higor »

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**
When coding clientside stuff that interacts with server, always think this way, replication must always come into mind even at early coding stages:

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.
User avatar
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

Post by Voodoo Doll »

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:

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
	}
}
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:

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);
			}
}
to enable this add bTrackerLiveFeed=True string to hercfg.ini
Higor
Godlike
Posts: 1866
Joined: Sun Mar 04, 2012 6:47 pm

Re: Replication of weapon created from scratch: need help

Post by Higor »

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
}
Post Reply