From 36f1936667d7cabc211e0a77c611db9d54b73981 Mon Sep 17 00:00:00 2001
From: Allanis <allanis@saracraft.net>
Date: Fri, 15 Feb 2013 07:31:59 +0000
Subject: [PATCH] [Add] Secondary weapons. [Add] Preliminary seeker missile.

---
 dat/fleet.xml          |   4 +-
 dat/outfit.xml         |  36 ++++++++++++--
 dat/ship.xml           |   8 +--
 gfx/outfit/missile.png | Bin 0 -> 13108 bytes
 src/ai.c               | 109 +++++++++++++++++++++++++----------------
 src/outfit.c           |  92 +++++++++++++++++++++++++++++-----
 src/outfit.h           |  13 +++--
 src/pilot.c            |  50 ++++++++++++++++---
 src/pilot.h            |   6 ++-
 src/player.c           | 108 ++++++++++++++++++++++++++++++----------
 src/ship.c             |   2 +-
 src/weapon.c           |  94 ++++++++++++++++++++++++++---------
 src/weapon.h           |   3 +-
 13 files changed, 397 insertions(+), 128 deletions(-)
 create mode 100644 gfx/outfit/missile.png

diff --git a/dat/fleet.xml b/dat/fleet.xml
index e5c1322..90954cd 100644
--- a/dat/fleet.xml
+++ b/dat/fleet.xml
@@ -18,10 +18,8 @@
     <ai>merchant</ai>
     <faction>Merchant</faction>
     <pilots>
+      <pilot chance='100'>Merchant Ship</pilot>
       <pilot chance='80'>Merchant Ship</pilot>
-      <pilot chance='80'>Merchant Ship</pilot>
-      <pilot chance='60'>Merchant Ship</pilot>
-      <pilot chance='60'>Merchant Ship</pilot>
     </pilots>
   </fleet>
   <fleet name="Pirate">
diff --git a/dat/outfit.xml b/dat/outfit.xml
index b368d2e..baae407 100644
--- a/dat/outfit.xml
+++ b/dat/outfit.xml
@@ -1,13 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Outfits>
-	<outfit name="laser">
+	<outfit name="Laser">
 		<general>
 			<max>5</max>
 			<tech>2</tech>
-      <mass>1</mass>
+      <mass>5</mass>
 		</general>
-    <specific type = "1">
-      <sound>laser</sound>
+    <specific type="1">
       <gfx>lasergreen</gfx>
       <delay>500</delay>
       <speed>550</speed>
@@ -19,4 +18,33 @@
       </damage>
     </specific>
 	</outfit>
+  <outfit name="Missile Launcher">
+		<general>
+			<max>2</max>
+			<tech>4</tech>
+      <mass>10</mass>
+		</general>
+    <specific type="5" secondary="1">
+      <ammo>Missile</ammo>
+      <delay>1200</delay>
+    </specific>
+  </outfit>
+  <outfit name="Missile">
+		<general>
+			<max>60</max>
+			<tech>2</tech>
+      <mass>1</mass>
+		</general>
+    <specific type="6">
+      <gfx>missile</gfx>
+      <duration>5</duration>
+      <thrust>1200</thrust>
+      <turn>200</turn>
+      <speed>600</speed>
+      <damage>
+        <armour>25</armour>
+        <shield>20</shield>
+      </damage>
+    </specific>
+  </outfit>
 </Outfits>
diff --git a/dat/ship.xml b/dat/ship.xml
index 7be7fb1..f3d9123 100644
--- a/dat/ship.xml
+++ b/dat/ship.xml
@@ -24,7 +24,7 @@
 			<cap_cargo>25</cap_cargo>
 		</characteristics>
 		<outfits>
-			<outfit quantity='1'>laser</outfit>
+			<outfit quantity='1'>Laser</outfit>
 		</outfits>
   </ship>
   <ship name = "Leapard">
@@ -51,7 +51,7 @@
       <cap_cargo>10</cap_cargo>
     </characteristics>
     <outfits>
-      <outfit quantity ='2'>laser</outfit>
+      <outfit quantity ='2'>Laser</outfit>
     </outfits>
   </ship>
 	<ship name="Lancer">
@@ -78,7 +78,9 @@
 			<cap_cargo>40</cap_cargo>
 			</characteristics>
 		<outfits>
-      <outfit quantity='4'>laser</outfit>
+      <outfit quantity='3'>Laser</outfit>
+      <outfit quantity='2'>Missile Launcher</outfit>
+      <outfit quantity='20'>Missile</outfit>
 		</outfits>
 	</ship>
 </Ships>
diff --git a/gfx/outfit/missile.png b/gfx/outfit/missile.png
new file mode 100644
index 0000000000000000000000000000000000000000..10430afd71ab19118cd7b2d41262cf5c726517cf
GIT binary patch
literal 13108
zcmaKT2Q=34+xIP!><~gok|ZM{dxlW<-ZOje8L~-|y_0mC-S(F3P4-UNGkf!1&ilOQ
zea`co^Z#|E!|nF_eZSZDbA85DsQhb5JZuVV1OkEgQc7GAK9^p7U}3`F0zH^d;S;8z
zjHEc?^6F1UT}}*q<))pKh9d%jLwxmthDb^whcDi6dim<bjZrN0o3z|bKgW0x2wKET
zaS>&=iH%fOb&}DG%f9Xdd}d}D4G-2^3g79_Zb$mpQPzB;EoTh5Zb?XKL2|o>A&lFC
z@HV>zJ(eNbBWx*2^$EIl7A^Jip5Cn}+s(tO!^676R&f_8o5g+pGxYA-zT=d`6vxHx
zls?4Ufd6*w>fwa9oJ9EsZxha)X*~Bj`o8w=Ch_}=oXdN^4SpUASWT3Ml8}>Ek_H*L
zI66Akq4s-I`Ias(&QB*FJb17+IXU^jjo@6bw>6UT$$dJyMR}xh`<UjVM~~8@rM{?@
zm6dgOc6Lg(j?}nqe{K+`t5Va{q|i`SRz6I>h!4@4>+8uB3-+q1so}0DFZXzv{mZOO
zGm=8)F+V?55|{PF>f+*}we4(uWVP?5PY|k<pP&E7RF$K~$jHcnboSHT;XJjx;*yf|
ziwXP6%u_7hRD3S0F@5x=e`Gp3I*kPd1=YpH#gSXH4e<xDL7|~HvGDQn#R=#4$@IMD
z&_(1$wX`PNlai9WMKkSI^3;k-y>*HLamapVNk+ZrW@jI8b#}%pLLyO2tgJNl@XVM-
zt-E<e^_sSC-c*#KP`4g2GL9vrrTH8hy+0kKoapN#NaAs1sHm^kb=dy%qG{GwjxPGg
z@<3JxDJdycfvSwx_Mbp0Mv?=KE$4;K*n(%zo;k6^dqy_u%c?NND$x^#YRSlS351F}
zY){v?^1y9x4bC`petMLNhK43*#yPP@KqEND&BaB18#_=2p6pX=SkdbobSGZ#1aZ2X
zTg%IgkC>UM4(2<EBT<to*@^;Wq2k&BjR-!tyw~k7NBZ@Aj<cKo%{BQ}JFmq_y10~I
zjK1c4(@WJlhB8{(b*uFc2?<fm#})9sID27ZV?*Z7Ka=$KErQOU*2bc2LX0l@`QhH4
zU97Z;xtf|9DTc@_0->Cq%WeLP{y8(V6=A4&(#HDw`sDQV-RYqrCpl))hg{E|37l-V
z(RfBG^btRNee`{NJyt+KAVebTPX2%youZNwN93K!Z{HXV_4W1rrRcF?e}d}j>MFv+
zaR$eNW*U4O?k4wK)PMOxKQlE|i$bB;myDv$<GBg5BqIHDUZ;AbbS<!~tgMW`q9sH`
zOJT6#qF?hv&|+O9+!G4^%!n;da$U0^u4BQ0c*tU(7^!?BRWyp8`uc`4)9qYkCTI9n
z(%FjR10y4<JHHzP;(XOI&d$yrF@~MIr{Zzg62dR*ihJ@G3*#C$e(>MdnetO;h)s{W
znY$u{Y6!%u0##Qj`j6?bAkMb7wz0}g1>fNEIX~HdNYK<3&-(EA^fc;^-E$>Ej6`_c
z5Ta1}@V39+NM)v?0*w;WL=*)#56^gV+usLfoJsZ@8ygQF5Oru!N0CjAjJTXNhZ2{y
z{(2=XU0i}fRgDY}UmqSC>h9|8CB>)aPxu%YXR6GxHB~h@F)+ZMQ(b*>YFmHIAd-Dz
z?cLw7o_P<;pAchPjVU&4W=7YRE*k6NLJXD&x9QT@42psf!_ULh6AzyMJyUF_R?Z+=
zoi4$UMVUz6u!S>ks%YMD5)%pbBgAh#3SN483=0d3od<+iC5noQB+5*~0aEnU#8?=v
zbkXe`<hV417|7L?737E+=P|q?nK+$7S$zB`I);d@?PR%i;}+h0fmVgY;{5z~B}K(z
z1VX<j>6w$7+9=A@)KtlU9AA=9{83DYjZF5h<EpBvJ{m!<5Ua`Z4Z}BY-XyY9xXjjj
z&s_7<=R@fGt1}A@PED=HrKC(9ccqV#cgbtBkgWqKBq5P2iRI<xIJPsjFQsH;2GJ10
zuU<te^whfVn#w$Un3Mia)Ie2Lb>#i5#iH+|7&_v|K$hg;2PyjWgoFfh8b><J0JQga
z`CLSbMjIl-!cG<smCeSBP4@iIZ@3JuI0Z;#b%-jIcIvY!BZpU4CnhFVAFCBQ<qfZ1
zI;J!<2x)6;TOEDUpQ&}{s7ImfDG<NkB_@uOFvU_}ipVEx7Sul=4d;0J^r`OB($e8E
zwy2X6r+aI}oz2Y5OcMm+yHOiHE8HXoygpN&vA3+WlxnQbvjQu8WisscV>3ViNk9P3
zO!-Q;wUGjX?@7FcIyG^8X8jrKg@uKmYh1Tjd{6dPwk-O5`GRe%tp(sX2zsPe=j7ZF
zmyq!J)$cj?^ZGDSDd#I8*1Npz?d?J1r{j`$50R#rh_|q8joK{SZ>8v4&C4c|t*x#3
z4mPK??!|XSPJDQpXvGpJ%2VmM)Qbgxxo^egSuBxNH8nQ2Oie>GYawgR!NC!v%(Nk*
zKw>ppKi$^UbRQ2v>wbAr=PN2I`dHQUeMAK3_Tk|rs-mLeq$Qjzx?!~Ya-ct$fW~Gk
zN(Be0tXVj^<~lYuCj2xJw?$elXE4<mrGDS|sm9LELKmx1cGrSKx{8`wvYVS5Ka^X%
zmX=ms{mE(`gV9=2LIN8=4wGqLTHSPGqu<>8ypvS6;$6>+v*R?5H@(Y|1vFGtxB}iM
zBs!IL_biY8%^ez$Z!)v6T<nY#Xtn30kX?Iz*;O&?Q<IaIw>GV&=e^IJ&ncJ6YtlAn
zR|utn`%#LX1&5UR=xwZvm6cUWUY;dURNE<Sv{b!zwbKeQvlHN1FpFmCGDe`NMynyJ
ze$MW>oq@p|*$o2fvw0Q9vw=gQ?{;%P@2V#q$MckaFE4k=SIzUj(ezy?ZXzx&P8v}E
zHE(L+*7~|va4ZXk$QC)S1X+C7lZD-5QfLylybVkd|KejV2<PYL$>GqlGCX)NY>0eJ
z8m_l{e5MSWf^?{=efIb<4zZG}i;G2VZSC{=`ue8so}L2cCr@VesvNR0T(7;>E3na@
zK@FI3nn9H^jE|2~!p<3OtNv*GkJoX@@8#u?hQ-t9vlTrW0pJKNDLjtr-+%G>oK?zb
zYfp*i@5MdQGDUgpuRc>>f%kvT&TjuC&brclcTvPwN(@fVzhN~!YdbqTeIu6_KmVa}
zbD4-#J)n{@#pyDQvfGw+r;*C52;B2PMdZ+J!0!?TzYgQgu^Ly_ErK`8pFVwB!45Pu
zGcwAe3Kve{b?($+h&fhKRvt1MnPBAo3SHsmV*$XTdqkmM)mQcp4n6}$vAORU6XbL9
z@$&BE7Zm)xkPi}c-}#aOyDV;Rzs;3@&A`T{UWA@VXL!XaVL323SZUAW{LR)(U0!p4
zzwy&V*+vI!VOqk@sp)A+{p|SkbYDEg_vZ`@LA$|hPV(}E&2x6Ws>;eKXJzXjykB^E
z@dMWOx*hK<JjlPc1*bf3a7A-+VuD27oDS>S9b8P=EoWXnJ{fgpRa#XKU{4#Vsi`~{
z7Z>5D!~G=K*HfO`8xDPZAQQ*U$*ErFb-Y7JidJd=x0OUsZx$n3YVW8gnLA-V|GC@0
z-%G$_9y{^s{QdeWSzX=C22Pz{iy<E1v#R8Ks+%pMj_YHncbl7=_*njea<5+LtZZ#X
zb!b<A{!Di}Xl|I>c1HVCLPGlrnb;OIe%h$EE(wJ_X51zoK|w(YIX*&ia;aZy`=<!R
z8kA4~922sLf$2*shM1~^7Q?{Aq$Honflo{kO><Uo3V`X+%ktuQdU~qK$@STmX%;+=
zl$e~`+Unyd|C*dk$?0=;^aRVlk_hYFAn>`ybHnBQ{Cve&>Da@$reMWuer(Dt@s!j3
z*RNma&6Fp9NEEt?hbb0@QnWw6%!ei!E1LO@iG@X%A*O?kUCqH^rwXu_6*%Lr&2;tc
zw}D@^S&CGU%IMCp9&4jTOKC#B^-W_*aB$3AU0o*tWmup`PsKh~o4J187)tywj+)o$
zo6FEQg*}u}n_>CD4s;KKtA`9W(Ge39OMEDoHgp{mQ_}!h0|h+Z7DeM5<UC0&;IX$@
z?|sTPUCV3m6Jryg^b<Qf`)0ynzrDSExiZtfor%dp=hT$;@ZYwmTg*&%iHZMy9kvLN
zk&$6Fx?{!4TI2_ZJg>M|@m)4c*k`t|&k0+daa{{9WEnUJWqR`SnNiv-4GW8lq>;!>
zfDYWM%E}aLOUu)_)#1Exb=8UC;b*xuHK)CUIdVe!$gZVbV17DC(M)0X15%8Wd%m5l
zgYCkY0e(muF|j6drr2sT;Eq<^SJ=1L!Wf3n`1o=7utiydip#GrFJ*aC>njTjH*rVs
zx=l-NJdW*D%y-VbL~|75e)jA(wD!gi_r%yz4X1%A#_AT?{yRL>dl#CiR-kEOX=LR1
z1&K7@hn`-I%Fl0Txb&*iW%bxw9#CR-enf&z8`FVih$7!z7yNhmwknf`-|Zh-(3~j&
z0fB7JU7}Eokd{A#UV^@5H8p%z%3?rH#n3TvpASw>o*w;a3BRMRIy5kFs~CmSctRT9
zS5{tb$h#gz&h9V65R*zlNhx#M*iYyu{9~#g%O64VLdM$4DoDQo2)}pg>zv)DpFe*_
z7%n%oIYI*nf5OZhG%_@le>;?|j{BOP*LDjdL(Iy_=5)<t^&4YNO-6*cO#-ZX_4!Sj
z$-=b4;&doI2SN+ZBswLfLE(JoBHg;&Au~=j0e=3Gy{b&ttr>y6{k2gcJ?JDqM@J8(
zfWoxS&l`77@p~RvVIdG+US5Vyywe4xrJU>|Xrx#e0S4qglf%O%KQxo2=zr1^A@y*l
zr>AwAji27wJ@)F}4-_?EO7ElLwj5!u(33E$l*2s-!GNDBKlUnTa0{B-^4+_4eShXE
zQgYjr&X$@h4K@W6$c>MU$pSqW3^$Qq>P=lMvz-;R#K8#{uG7_<p!rHi6iS#VyGbU7
z6Zn&xfq@~VzhA!L?98hHN{m5u0*OQ}7(YEehTo%<#_#^o5%$f<+i)3Z?;(6o3k{)T
z#Z}YP-JQR)K3;;auFS;8r=_Z?=^@M%8<d=w7!6v_^&Ts$?QK;N6O{Vs`PrG~lP6Ce
z-o)@{*U->-=(;hn5E6I*5;1tX-kYzjug{hvxC!NcvS;2qQ|BpXq;gpDrcdIDUi}#*
zX}C!OkE5vp@VpM7Frw^FOU~h-+BjBC2|s?Ehr+lf(r#KZD42_W)Xq24SzgXn6B2@z
z3Ke%$)NoOKD?BXr(<diM35m3+GD|Y80@W;j>L^n-ayH6?7Q^j+&`7l0+!etdbr*rE
zdCsDQ7%?-yT3aP-3=FnKnMnx%y;Ly+=B^_$@bap&7@7LbII&?3VR_Ya<QY|IgnX!o
z&o?)1P}14I2t%8%!(KRoVsSMwF?oha5o9MN7xJm~2I10xdkWI(f$!#2)&0}Fi@CYE
zvy2Q{-c1au|8%7Y;Zt&19a(_1q{`n@XC|d~N||kutmD+t)6?tFXRA-<b!KAV8G(}i
zO3%O`@v+0WHqg$8m}Va{QHnm-(ZxlVkci08=k#DBzLn*ZEJGd;xtVIaIZ+yv`kAAp
zWuC%*ZCzdB6<|CE5Lg9dbNX9;#L8VNk%h)gx>i+JcV0ovAX}Ln8*4ITEsq^|jT|@V
z6i9i^&!(nS=$vVs+}xC}ib_h9ENyI9p0cx7@qt_nY5RM--hH?A8>KzGCBN0qe8&h0
zcJ{{k>0u=?igS;3@VyOiv`A>R2Y{3HNl#6%@7=#IW}fd~R#CBL#;M60({aYj%p6gq
zn&<eYFRiDutBXUL?n8Ao@2yCQnwQy%6$Dhz?OfE=$K(j8`Ip&M&l6<7Mzk2VoSvL)
zCi1%%cTP^$>;RX<ocfsFYAYREZP*ef4E-k^n(7M3Ea&Y%EefqHzd=Jik&GbU9vU82
z$pMh=OBbSX*_`4jL+$zd`(r`F;{(MK@9;cj!--cwmXV~@cI>-|8E)OmV9pI*Uf#_L
zAQwg`_0ZwrVYPz%{3)ob)8^)8!MY5FS4pMay}dSJfq^e>CCd7!D=8`2t^9g0-_X(D
zJiF6p{M4N`R=U8_($aBrb@ivQZ?ZU@0Rq7c%POnRJhC7Y&w`9Z#>dA8&o3-&jFnl&
zV+GC0C^M;KspcJ$%WMVH_%MDiDZ#<R!@Fy3YunJ)(P5q(7#N7Gs&dCi=zHi{OxJlH
zin_TSMe(NEgo4_*E+;2f0=qqnjxZ*~!r+OOjwA|ixd}zKTq5TcK_irAWNFz29?ec}
zZf@G2KYx6aIyWZDy7Ke#WTHQQ{Pi&1;H{65?G;)1{pSzS*2)UcGY$?%HxG~MnFLHD
zHgbIs!;WD3SOSRIDlvXS2MHw0eh|1f?K*26Y~ZE<OArtl8JQ?fmsnxY6r^cn(c01y
z?d|2&3A|OO-PqxN*k`@?PoJ~}?Ei?T`#J`+{S^UQU~kT$N~x!(Cnqs6(c9F*0^8Zu
zwaCic{MhGkbGo>`|0V$ejit49-KVCx2tzh<W!}^osjPwAm-LFy5E)6pLl_9L+JP)!
z8rdv25EBuJCUKholDX#hq26U<LOeex1`LKGG;~bk`&h+B`>Vqea^`GM#}=-x6)t1n
z4NxvFF7_0Wref04ofQiU3lan2k&zF8(MW=e^))OEyScU1lq|Vt@!RlfGb~$Pj$Ard
zXPa?r6wMa5*U>*i-qec^uXB>w$Rp6i>DVRnhE1^of7bB1Zl2BaGjmel$)-FX0M;@Q
z|2oH$jU1N%n$Q~y3tke0{#)8_u~PJ^O>Ei)qicCrMgm}=vw?v@7wqkYtELm2t-f~S
zos;(V_H1ucGXx^!{rdw-PBVF3YwL{=IB^XvokPi7))dPN3;XIU@wS=`KIbO+POebo
zQ;m)H@DTs<Yis#=!Dm?3JE0Kp;N&;$PUwLCOO_~W+HPFj4DgZ{&!V%cWTd63DcalH
zdjaji+s3Alo`t1%WMaYtJGm!KB2zwy5;tg+DR#A7p0PG2E>2ZVPmkbZObidWTQZLy
zKb8Yka$ZQe4-Q(;(dnsgpJ_=0QK<McS%$#?(M&g}I&ce?(VD)OH0t-S?Vs=h(iRMu
z<>FzY2TNza_B`InZadyx@;vG82R-Zy@)6h~@vp(bU{e#5mG7MK;o)80(NZLLDJebg
zKAzr~tVrhJ<<-qlMAm~<mR{j~dT@=|Dd$x(X7Iar-M<~G_!LO8VRP=GGBSRzLfdwR
zUilaFemKAe<%3rmqN4tR!n7e7gDZnr7(c;z)EOKcjPd*YEvfBq0Z_lMR|LoBa5Vx*
zES#qPn1^x~eNWBR)ycv5Yi|S)Ko2q?!1A9014C3{s4OSv%^DE$gF|HIQeV2<9S&1j
z`!{cvA}cE^r`2bAsQKOON>fvHB!Hd$fp@-m8@mboJ_BCYZ1+5+8L+z<$SSqRzu$fa
zu<H@PlBtb+3~fNHz5DRt0}mDcn_$fXhSKtK2G9{T(H--lSNkrnruJs2Y!}o)3G}Cj
zij1Gk2{8f>4-PD<tE)wch=|y_I;)L4K5i&8J$wG};TvtBsh3qB1RsrxYiQgdA|#am
zv%Kuq+u6yKmyv-eNpX#WzDGkuq-bhu>)Qh`%M89mkSqf_6s8n2*NkKO$%%&@$RvO8
zPlSrg%U?U$*@gVu*<r%P#y&A`^uM08)o?ju7OADG8Z=YmTGajXC*5#oXYQ5F^-Vb!
zD_Y9w@5%leLB!o%U?s1h+wcT8y*%9Ck5ok}kKV>^DM(47>i+nSBT?2F@Z@~5!uIir
z6aIOWc9p{dmOpJ)kxmWiKX5spV2B`IpgP<<0qe(s=#Yhng_+V~Jq4F=U=Dn09Kf8&
zLhrq0g>qq9DNuPO&nP10a|XRh@7xI;=f>UL-ECM|T@8bpK+421^Mu6OczM+#3ko(C
zK7anK0%yYFhA-_&^A*95P{@*~d&J7x00w9N(2CP*046P`b6&2&Ne~^9O2{E%u<vri
z!otR(C3gR$cO%3wG%`B$IT(MlCQoAh-1A`l1Dr!V(uoq&9)Ec66D1XuVI<V$?6v2J
zx3fq33$tnOq?7@1CPBO>?|FC!2L`sprKM40A@uibPUbseyof?eLqs#3VmjtGX?)MN
z*;N}$N=v^y<L35lG=54J9~(Q}(bd(oj+~67<i-UXCyQNn|8SIjFM-|YHCP1?;7bpn
zgS6DRZ2VkzY~f9P4_sXwS|*2Xok#Y!qM|mdvKRD3`(XCo<8xVGj&E!0OA`>Y$DuUn
zwExq5la($yM7zPK?#iI9zL3Iw|KFm7|M<^ccEW#zzt5syf|)t6;uQAv>({6gUo3+0
zbYhSN&?ZGBC2yD}0Tnn86Q(`W274Kpo$=nQ<SU!k$k1>TB#FG1mdq6Bkukl=O!U7a
zM^38w>LMw?Exx8UK|%FF39x|ONNJkRC#+DK)MF}?Ibas+<KrW4v<AA0f}ZH5m#62c
z6jA7WOj=sKCN1Gv8SY(b>etPRQPSD|KYskkF=~6C$`10b`;R#rc^Ys&W%3{#ER4T>
zR8iCdoezjYqd~ai!Mcgl6D?6{?G7Q8HW|ZAZ<CPZD>ITbt*)(^G{ikjIhr+IJUVi#
z4-c2w|8CGkRc<}W@T`0W{`sRjkwX9tK}YhU2$zD>%nA%iY$FTc;lJXgvl*ORTzqEw
z`|UJx248&#x6Ini%#1y5vC45tP?B(da%X2pOGQPc<clIwNk`}IY&+Qpj!<!rGkAD`
zS)Wtu3DyRiv$DT}o&8YG_X6L&V`eAEwIyZMJzKqBR5;BT_8C%+8I$fXsH|IpmuHS>
z$vrL)39+c*JDe0x9<s2o`~ef(J8jS%B%&R#FIui-0ibNI7IJa~ThMG0-@kwV4A$AX
zHFpW5wAYxGi@!Dt4II<mS0Hrf%PdEYp@Yb1YHBjJ_D6(;(P3g@GBCz=ssMkA2D`rc
zw2P{IHJ&C55Z;FmKX`W#j<+26NsHaKJaHhQ?n^{uJm=%P;ZUW`hZ$fX4Q?ndW`HnV
z^#3*YIhLJH@auFh=WDj=JuyTG-_kB`?J~Xn%qUEVL5_==F}jB4Pm2heGhhtUgoZRd
zTWlgTw}l)7%&&V+^}1L!Phba@>zbeN@76iHsYfJXe25=9MWZ$MvHs)^nBiKKRjabZ
zt0^Fd$f)oo<rNg}T{Khsql<hn1yg@@p({S-?EGBP{&~tHEPq+>yPFRV4m^PkS%!y)
zrwe)=$$P@<GJgH~h4P-R?C9v2;iqO5$r^|N`YbpO0mdrq|0Una(C8?oNwBw~;>Xm;
z$eyyoLY8mv@f*rX^er0jhSJ5|V<B++wXdR>%vlfqTaXfB{rSPRZo@`SMTo)vg7DpE
zMWhrx(ap0RqmjWubRd?gub1$e{t4Zg&}TycH2TvDL(~DTKBO0+vJ89>ca%gu>4tnS
zgqoWAK$~SWwP~&r4H2~FRK1NEaL?@D??z>k7ecgHirI?DT!;aDY;Cs!VaId9QH)b7
z(!Ga3{Htg<?-`t!IOzqw=HqJN2Ni{xoRDzF7%kNcAQB51!_Qk73Y`l(lZr^E3t*$^
zLePcNdxe0J@FRq0ayS>k)x8J0%&tmXS1e~R^uMfqaXKD&<5d^9jn_s=aL4GR;n%OY
zde6|%UIYXV^18Zdb_<=iWfjG&W@^WOb#$!cfPb0~U2{{O$@i2@PH?r?YvA*XO<i5x
zo51PFpyMZ>v$q+Wnqoo~zp;ii^GQs{g~>$-<c`1Ksp{Fdxd}|3Ci-zw^n^=i6WzoR
zMaOMY!3@a6e=+cipL%N(x<091wG&b+>$op86c+p~EtXkXgTS<=vht_In3zFvmUzB!
ziLAhw4&zvD79ocJviT|b2wFkPPU({uf-e9{lPX)$y%2iK4+t3|k~r)DslEpUh*<##
zEr4t?S|aNeP+Xm>xFTz*>wdJ<@1KD<y+wKro7BMe%Ejp(L6oX_akTw+HWoYJDB7<O
z6XA8N^_rOk8wb*vAVs7yZOr+a@jswV)bKdn`fT%g%DH=R99w~p;6QWSLm++_KWzk)
zC9g``=B0(js-(BgMuq8Rijc3M8ZBWrc++W(`fL)nLYt-Qbkhr!nUHYU(IJIPFMZQT
zosFX80#2xuH_QOW_z!xb4hoSE-{4T>7!GiB8b5{J6mbg+!$_apCcmiY@apV6CY^wA
zuFITL(?VES*b!1P0|f<z`K@Oq3Tix`v$CEc-iGO|w8$V3mt0yoibxE|j>!}iPY!TM
zthgwFH`^x&)2iD7S&r^7uE#_ehlzhy0h1F+7T!WDC^(~7G%5qJsMSz+lI2MLww<G+
z3{by81Y$N+{PR60-g7pPV2wZJ!C`ufajnsTB9iN!bT$`kmY|<-hGE%L6@ad*kow1*
z9o08w3v*!A4<r3ARk~bgx_<bf->uZ%l%k7vL)F&{?wlR(?)cj0SPx0sL9|f1y|*_7
z1|46zM3y%zX}I@DzJ?M30)cUD&Jly%#{~sUI0^sG)A@Wj;_cwZA|WV%(rRR2VJV~z
zcY&si2`4b+t2`qsE~c2+;P78%1cIpSY8C+6R2LjLZ^e-(X9L`=l~q-Z_wL<W12=&y
zIVovIfsy2fk&)3Fily!Ii|}h#0=EuSsI{bIo4-n)s<S?l2HF}P0YL-2%1Bc~gNH#-
zQ1{~SHb-sn1@ZRyoAg(ZFb%o5x_VlV7r*c?s}Bske&xi>!z0JQW*O@0&dlAHe6OEm
z^^}n@gwtZ^+tytws{FOpRZ$Q?C+nWiAonK&>2NJc!1F*5+)WhXZTs@_GEsil?~M&S
zNpW#)YA!2veNa)$xhi=RCp%|XKS9UH$oQ#mi5q4Ba0S4QuNUehq@bwonw=H;URLJp
z1Q+ED({o?V`m>#`e}Enh{4VPWfhf*@qmuS)0bL6doX?(l*F(hmnxEqNf4nZe6Oxid
zsqk+o^?=kq220oQU}6KjzbbQcb2Rl4_9stzOdwj6&7Ul0?cMEo$m1}NH9RvTs0z`!
z0#hs(6e@S5&M^>ueR#ey#;{@>930!bU<5~K7I5X7f)~vN2ziSx+N@MGb4&*S!5!@X
ziq;{x=dqiD>02-WK!t_?3gub81b*8>>#x(rWZMMr?n;21b2&Vx3IursWQuhjTr~;r
zVNNEdFb)XyDzsS+UxR@Ir;p*_Fl{Ynwme2Un+-!m02lMGIjD*>aK`c2%QFK-=Ws&B
zKQhEhOG4IMbVV^Vj4UB_zxx=-YVPP7A0fto2w^jL@zUAVW50ems3DcP9>sRv2eWfL
z!NZ}73Y3pzqU?ccro5M#wRP(F%#4jP2SA>=GURPls(H`8DKqK1yC2`{pM}87d<9HA
z8PM2^AhW2NMRx&Bk|8bhjA_(=dmV>VzgXrNFiN5A{oPaQt}z>kP)|VC(HJ4CK!o%|
zC?0Q^`OI-q_7A{Y=<Z?<r6a6aQh$HH+^VsVds`=X>=+c0i~5k!9050Iz(i<sxDY~;
zh{YHtp`xl<xW2N2HtT(uN)i5h(5w`ZDX$)uT?KLWEsRD^c5iog_YSbG`Qi6>d7T}h
z6dXTJ)w*AuOPCrNha7bo?ye2A%$xnLPe$z@?tO@j9fn9FNv0v;4sk2oHEan-t;aqS
zWmk}df|H%6C*Ri~E^=JLksuZ(rjM&m)ue!8zWJPx?cj=L{=<#$(m8#!bWAS>-jZ*q
zIGsJXq0^xzKfupWt0*f=%_}P6*3afKZYTT-1~o*8%~So1VMJ^r7(g}>`j!Y96)V1c
znGe$}psuo+=5_7H1Bq)XlQn>^rlL{+4wI^Z$_f}5+;zG%+8P?}ce?aXkB{*<$#HeS
zh@^|?xo%_)zQFS4CJT5Ye(DyGvxj6Wt~?<li#L<S6R`U&+}_f%U<Ken04(vz*QWZK
z8qZG<HaLRwNT9x|sik#edH>|s`}g=HMz$j72FsvDA($aFGc^rD+JH<k#||tugLwHW
zO#r6WLWqI)Ek8fvyv!=Yy8|q?L|jZGd+^Nf<WnXz&EamYtUPMbBnvVCwbqTN1MCpD
z={qeOTR*l8)cRKaFEdU&pylBaHp}ncy=#$-qK-tBl?ecADn6Mgvz%5}*@NQnZw$E6
z$4!p=8NM&j^0Xbejy&Th1X4N7-H;T^U!OdPoqKP0sZR)jNCNwxJ=LXs!2yO_f(L;V
z<TYHJSRTMmzk)OX5K12X+O=}~swrY+rdU9!Z>_l>0R`Rz<Hdsp+4hqDBP6te2kPc+
z&COnENl6~*qM6TmczCJ`)lomMJ>CEiU;`bD#T09i`0*nV=ws2J(x*V?j6l+MwzYwB
z5%7F%ludc(&PyjpM=^EgIEbiDZ@?bZNwDuh&mVg6;ze*GxR@QrpRxSi^xbWT#>U*D
zB(kDmP^l?Fmci6@78kP#&p#s?93#jM^ng}jegE|p$Uc<P8(1{Km=0O+U^9)LGqu=1
zry>CF8fJ2WVE>GEE~FhDOZ;Bv*uHt1C=C1!((^)vt4SU&EgG8ib3>Gyx3zVF3^?@I
zs*S1Kwz@HjNbw;H=Omc9Aq{VF{r*eP?)Q&zptI=kw$6}jRHmh+Vds}ZCG`SWJ%R8_
zfu5*pW#8k0>V!j;m!r4$Id4E?#+9@D8=QGrn;?*+l49_a-LR8{(CB*G+ua72=zxj!
zgAM!?(q<aW8#j#7ezVij{p12e*mJo*GdhFjN*r&1mH1^Z_yATa?qb8%H*bw#0~E%D
zsj+c}(pTB-)!<~i4{2!^d;OVWvwQ9Y0ya~XfzVVNVQuo#1ii!di*J(y0hh)MP)G$E
zF{+HqPk6cF_WOn+hYua|KF1860K1PdMCb?zqJ;CyOrG8`GdF+f3?@1W6;<~phuDo9
zH?(jmxtOa;N*rY%TlUO<y!hvjXn9$g4hsZP2W>5!`acks@D#~VGCu5_(Ngaokw`a?
zYi7!45MtbhKpk&|;5^;;f+w}b&|D&tqAa5iSHNoQ`?;53!#ubpIVV|)fIxE)QYfga
zA6fe@Lw7ud6}5&|<L<k>w(p@_G<v8-Z3{CI#)gJ}W@#oWY-dNDTwQajYicBm)tQ%L
z@C2Z_paH}EYhh_grd#Va&`%S)1Myr0ZF@F^Q5wF>I2itYA>x0M;jHDJb|~?JYTt{`
z1b0vOyE-!sSMoG#RWs<7tFoGY)04Dt*%5?~iyJXwQC0#u=;#bh?Dqb?2^a>^FigWU
zrH9wKw5wC5X>H@Xav$rnKkc`QhCLi?Y+ZRq5}<Hd8A5w8A3shETkLCq1Fw;8WtJ&_
zv=1ad{ZNhV1}-_Fc(!7n?Rar^RYRw~p`oFgme%RFhCP^KBNrij>ANyp<tRA<>7U_z
zd-TD%N)S9U12E9WbgSj9`qRSM2n+;5o28;IKi|sLH%^2pmI;Rb98Y8#a&uSfv{`Id
z`^Zbn%Cce8Q71CK0Q{f9YSlq34Qc>yclKR{`2#Z;n#npT!3ABM$*fa7MCFPO8T$Xa
zdi7GHe8MgNwnpLyVB%4Tf=>&xNZ%sgzxN4Jx#xQ{cOwr1-U04euYf<Vf_Br7zHxt3
zE!6HPD=7)zSXl`z|Ngy_HCl?xK%o-GS#HC7B)z3=r-gwoe1w~kRgX@Mivn~iZ*T7k
zq;f8CI2r5ak6iZkjSUecCF0Fax!+kX`QL^ueCu?Jm>)l$d_ol34RN-v<+^`MbF&)&
ziX;~&XM|$jFgIk8=a}Jd0em)K{^u32h<$+8gj$kP&qJGu==+jZ5TMAr{r-U_OH*c4
z6>~;lv<xSVe+DeaMM#&|&8@6BBPgC(WT9{t0S_O<bObv%ItnBwCpTOjN7BcS|1R=t
z)O%GGfbaSV0Dv2Ei2E>sDT*?~$X2ZK7tI`MVq*qExY{f_I5I*C_InVIVROi8LISz0
z-f!^p9w;JPJKmlpmmh#^8Uhh_l{YQGy_v!wMzdL?@7{9s{K3ZLZD(g^0V@0@{P->b
zcE|<(1l%CdCC6<Yw<IJX!GJreNC#>Pb|4Gnjq#vmY!~L{w)e(M-h`kcZW9p|lfO_X
zkY}Wy93Qt%V>b%=+0v2?tb|NrQW*uD!|$5k@4HcL_qEj3`46_Y&3pU$MvV-XVbqZW
zC;@tIUY;XNU?33k8XDsZFf+l&!_yKY^+g6OPL7k;aWJdG6V+B@YhYk+3a&_XcX#(S
zqh%ln(f~6WEYVWJFJE?e!VFIy1U|7)n6fbE(hD)|sTcO2m6e-Hj3f^uB}&eL;63D_
zdW{z?72ynqX)1(RKYM!%KWAiQ^cexMkND?STXw}65D1xLFP}eGamMQ;&d}whrO~s!
z6{W0sx5)-y-{P#SzoO7pdLamvHZx=F7#vgr0wwe0YaZ2QJG8%-K$?AD(nbFoE!0U4
z8J53&=g!K))>gVT8@WrqZruqpX%ftw*~!bx=Y#xAk;oeV@bTl0wR@F)1sV9tVxVkr
z$*?eL#;mv`3sfJHl9B1b1VL$G5hs|ULB-?iFK^&ejbV!rLJ;9Hw|lJA)7|}*J+b?L
zBp)Q_#lK7vlanW}kgmp=n3#C9WPH6q5xI8*PStCm*gz|zglS9Rq+5^&<?VZTmnkCi
zH^E+Ifmza<xVW<MT?_8P_;-1&NW?A;SsLz%1<enSjz&pIOM8UJB1en#g!&<bM*;ON
z1wGKEz5;_V-jy*iN)#9(eUK<-#l*xcKrh`pIH1@!q~I{^Hil4H2S$7D#dHt>fEB(c
zXa6IERQ6y6MV8kWY;_IIYN~Qz1kxVt;HJd?pLsBW@MwjM@F9!_rybr5nrSsHc>;2;
zgc^a6ef3I2T>M=q)G1|n%MRob)Fjv~sp53*!i4iyY~*`Dz+pBeKssA`4Q3?cq_c;i
z;Xeaa><}nAz#VjvZRdHFpBoz+<HAx78JM%Ux*nd=MKgkr!{5K+^bVqq@W=v68X8;Z
zM-F}xS;vq^i{l4#<bYyQ0n1upkWV(1_cs2`n~7I5O~fx0xWYb{11EY2i$oJ3lK%vP
zbd2C8jhjt&8JYz)Fm)22l$^`~akY}Bib~)`S~#YN&v~5jRHgkFI2S13e}@YW$3}so
z_b?D{U_2ELn01q22g+wF#xjzGy@4QSGnLPERq)Yl0OXF2VCD>SadB<J5|i6PY*$)Y
zIkw!|+tXuB{nWVK64DAwdLnZLU<~=-PHcUW&Q>T(7w{aJ92|6PYHMrz*tI|n-3K2#
z@Q<y~`JPf<^wzz8Kq+N6PftA;2Zsr9hL}5SxQ^)P=(oW-zTH|I)zQ<VVq$6<*PZmN
zE4ooX0_g7y%;#Q%i6!Fm^YeHx1*UpiT3%ox%q(p$E9-_BPwYp9y}j!UVBR4W%l{LM
zd6WVZbOc63=IG#n51w8OJ<-ZT#1BvK%wEzH)x%)X(HH6y&<l(3aAdbILYLRqIr)9g
z+(aZKBq&Jh_P}OuMUAi1z%X%EdHF#%_`Iqs`w3HD0>H!j!cHDB4ug3r%%o~$WMr)T
zaEb<8TOnOZffzP9<g-~g{2aV!1B|EA(o(PU#h&E6J9q9#;JnSsx*rl6dW)W(o-<FG
z>AS;%bTxR%<bJ{lUxqCjK6EV<!}EQCW{?8oz?*32%@6N*K=GTR-@wn8&l~oD?jkEA
zL-}t|PRJa^cm#v;?hxvEMoR2*q`sboSyg@kcJ^&a=F-EhSvlxlIOY}>ovscJypTe+
zeTt7~g{kA}D3iayS)*YvgcQOiYeSU!^w?PCc^i$dY*Aib)81`AAlpqqXwqQ1r~tzL
zp3Y9`QzZ%t1OiU2@$lFfBLNPMr;>ugb_E1pHf{O;2r*zDJDycfx6`yluGVea@D~_y
zY5I4B{e%(q7bj~A5Yd}CJMSj~gM0!<gqOMNyVdy9j};Y%@rbvP5O7`f_E`AN{%_Xy
zhfA`aAn}U7f0yTWm~VFg>Zt@{4Ob2mWn64KL|Tb#2L3;1W-e+Wc$<NC`%c{)hDkp}
zM=Jp0dbM?STHJ?lvu(klgo%lXjnYL%S{fHtmKg@hav;x11|JO<a?1YkapZa4ooYG9
zuh}^{>Cs&a_kE7$f<+(%8r0Xg1v5=7diAw;UJM+;VtoJrHcHdBAN=}?1e7|go7)Ho
z+513P6>#h>v<3Er{;aMh0T@)lf2$xJ>CnK%L~n<-5)~36T&2Cq&Jg3CRa>j=XL1Lc
z|NE&1-^-<2_a*pM^M-N6=?0Xt6+;_#PTnlS*xEf9<y*gEIWoS>Fi-R$DXH2EU|$Xl
zvX{+T^F`OEMju8*M4UmMaCdKKXJVG-Hbk=e!?`L;z&tCq-D=U?i2o|RmkW}jU)0dj
zlH*`!f8=|(siWqr%q%3Nrw%RgJ@>XAP8M#*tnX!=Uyms(Eg{xdw-9@1a{uqTs5}$m
YdjCV%n79;-Hw5D4i`U|XqWb>-2iDrW@Bjb+

literal 0
HcmV?d00001

diff --git a/src/ai.c b/src/ai.c
index a5b1d4a..758f08f 100644
--- a/src/ai.c
+++ b/src/ai.c
@@ -48,8 +48,10 @@
 
 // Call the AI function with name f.
 #define AI_LCALL(f)           (lua_getglobal(L, f), lua_pcall(L, 0, 0, 0))
-// Register a number constant n to name s (syntax is just like lua_register).
+// Register a number constant n to name s (syntax is just like lua_regfunc).
 #define lua_regnumber(l,s,n)  (lua_pushnumber(l,n), lua_setglobal(l,s))
+// Registers a C function.
+#define lua_regfunc(l,s,f)    (lua_pushcfunction(l,f), lua_setglobal(L,s))
 // L state, void* buf, int n size, char* s identifier.
 #define luaL_dobuffer(L,b,n,s) (luaL_loadbuffer(L,b,n,s) || lua_pcall(L, 0, LUA_MULTRET, 0))
 
@@ -60,6 +62,13 @@
 #define MAX_DIR_ERR 0.1*M_PI/180.
 #define MIN_VEL_ERR 0.5
 
+// Ai flags.
+#define ai_setFlag(f) (pilot_flags |= f)
+#define ai_isFlag(f)  (pilot_flags  & f)
+// Flags.
+#define AI_PRIMARY    (1<<0)  // Firing primary weapon.
+#define AI_SECONDARY  (1<<1)  // Firing secondary weapon.
+
 // file info.
 #define AI_PREFIX  "../scripts/ai/"
 #define AI_SUFFIX   ".lua"
@@ -114,6 +123,7 @@ static int ai_getrndplanet(lua_State* L);       // pointer getrndplanet()
 static int ai_hyperspace(lua_State* L);         // [number] hyperspace()
 // Combat.
 static int ai_combat(lua_State* L);             // combat(number)
+static int ai_settarget(lua_State* L);          // settarget(number)
 static int ai_shoot(lua_State* L);              // shoot(number) number = 1,2,3.
 static int ai_getenemy(lua_State* L);           // number getenemy().
 static int ai_hostile(lua_State* L);            // hostile(number).
@@ -130,7 +140,8 @@ static int ai_rng(lua_State* L);                // rng(number, number)
 static Pilot* cur_pilot     = NULL;
 static double pilot_acc     = 0.;
 static double pilot_turn    = 0.;
-static int    pilot_primary = 0;
+static int pilot_flags      = 0;
+static int pilot_target     = 0;
 
 // Destroy the AI part of the pilot.
 void ai_destroy(Pilot* p) {
@@ -190,47 +201,48 @@ static int ai_loadProfile(char* filename) {
 
   // Register C funstions in Lua.
   // Tasks.
-  lua_register(L, "pushtask",           ai_pushtask);
-  lua_register(L, "poptask",            ai_poptask);
-  lua_register(L, "taskname",           ai_taskname);
+  lua_regfunc(L, "pushtask",           ai_pushtask);
+  lua_regfunc(L, "poptask",            ai_poptask);
+  lua_regfunc(L, "taskname",           ai_taskname);
   // Consult.
-  lua_register(L, "gettarget",          ai_gettarget);
-  lua_register(L, "gettargetid",        ai_gettargetid);
-  lua_register(L, "armour",             ai_armour);
-  lua_register(L, "shield",             ai_shield);
-  lua_register(L, "parmour",            ai_parmour);
-  lua_register(L, "pshield",            ai_pshield);
-  lua_register(L, "getdist",            ai_getdistance);
-  lua_register(L, "getpos",             ai_getpos);
-  lua_register(L, "minbrakedist",       ai_minbrakedist);
+  lua_regfunc(L, "gettarget",          ai_gettarget);
+  lua_regfunc(L, "gettargetid",        ai_gettargetid);
+  lua_regfunc(L, "armour",             ai_armour);
+  lua_regfunc(L, "shield",             ai_shield);
+  lua_regfunc(L, "parmour",            ai_parmour);
+  lua_regfunc(L, "pshield",            ai_pshield);
+  lua_regfunc(L, "getdist",            ai_getdistance);
+  lua_regfunc(L, "getpos",             ai_getpos);
+  lua_regfunc(L, "minbrakedist",       ai_minbrakedist);
   // Boolean.
-  lua_register(L, "exists",             ai_exists);
-  lua_register(L, "ismaxvel",           ai_ismaxvel);
-  lua_register(L, "isstopped",          ai_isstopped);
-  lua_register(L, "isenemy",            ai_isenemy);
-  lua_register(L, "isally",             ai_isally);
-  lua_register(L, "incombat",           ai_incombat);
+  lua_regfunc(L, "exists",             ai_exists);
+  lua_regfunc(L, "ismaxvel",           ai_ismaxvel);
+  lua_regfunc(L, "isstopped",          ai_isstopped);
+  lua_regfunc(L, "isenemy",            ai_isenemy);
+  lua_regfunc(L, "isally",             ai_isally);
+  lua_regfunc(L, "incombat",           ai_incombat);
   // Movement.
-  lua_register(L, "accel",              ai_accel);
-  lua_register(L, "turn",               ai_turn);
-  lua_register(L, "face",               ai_face);
-  lua_register(L, "brake",              ai_brake);
-  lua_register(L, "getnearestplanet",   ai_getnearestplanet);
-  lua_register(L, "getrndplanet",       ai_getrndplanet);
-  lua_register(L, "hyperspace",         ai_hyperspace);
+  lua_regfunc(L, "accel",              ai_accel);
+  lua_regfunc(L, "turn",               ai_turn);
+  lua_regfunc(L, "face",               ai_face);
+  lua_regfunc(L, "brake",              ai_brake);
+  lua_regfunc(L, "getnearestplanet",   ai_getnearestplanet);
+  lua_regfunc(L, "getrndplanet",       ai_getrndplanet);
+  lua_regfunc(L, "hyperspace",         ai_hyperspace);
   // Combat.
-  lua_register(L, "combat",             ai_combat);
-  lua_register(L, "shoot",              ai_shoot);
-  lua_register(L, "getenemy",           ai_getenemy);
-  lua_register(L, "hostile",            ai_hostile);
+  lua_regfunc(L, "combat",             ai_combat);
+  lua_regfunc(L, "settarget",          ai_settarget);
+  lua_regfunc(L, "shoot",              ai_shoot);
+  lua_regfunc(L, "getenemy",           ai_getenemy);
+  lua_regfunc(L, "hostile",            ai_hostile);
   // Timers.
-  lua_register(L, "settimer",           ai_settimer);
-  lua_register(L, "timeup",             ai_timeup);
+  lua_regfunc(L, "settimer",           ai_settimer);
+  lua_regfunc(L, "timeup",             ai_timeup);
   // Misc.
-  lua_register(L, "createvect",         ai_createvect);
-  lua_register(L, "comm",               ai_comm);
-  lua_register(L, "broadcast",          ai_broadcast);
-  lua_register(L, "rng",                ai_rng);
+  lua_regfunc(L, "createvect",         ai_createvect);
+  lua_regfunc(L, "comm",               ai_comm);
+  lua_regfunc(L, "broadcast",          ai_broadcast);
+  lua_regfunc(L, "rng",                ai_rng);
 
 
   // Now load the file, since all the functions have been previously loaded.
@@ -277,7 +289,8 @@ void ai_think(Pilot* pilot) {
   
   // Clean up some variables.
   pilot_acc = pilot_turn = 0.;
-  pilot_primary = 0;
+  pilot_flags = 0;
+  pilot_target = 0;
 
   // Control function if pilot is idle or tick is up.
   if((cur_pilot->tcontrol < SDL_GetTicks()) || (cur_pilot->task == NULL)) {
@@ -298,8 +311,10 @@ void ai_think(Pilot* pilot) {
   if(pilot_turn) // Set the turning velocity.
     cur_pilot->solid->dir_vel -= cur_pilot->ship->turn * pilot_turn;
   vect_pset(&cur_pilot->solid->force, cur_pilot->ship->thrust * pilot_acc, cur_pilot->solid->dir);
-
-  if(pilot_primary) pilot_shoot(pilot, 0); // AMG, he's gunna shoot!
+  
+  // Fire weapons if needs be.
+  if(ai_isFlag(AI_PRIMARY)) pilot_shoot(pilot, pilot_target, 0); // Primary.
+  if(ai_isFlag(AI_SECONDARY)) pilot_shoot(pilot, pilot_target, 1); // Secondary.
 }
 
 // Pilot is attacked.
@@ -676,14 +691,22 @@ static int ai_combat(lua_State* L) {
   return 0;
 }
 
+// Set the pilots target.
+static int ai_settarget(lua_State* L) {
+  MIN_ARGS(1);
+
+  if(lua_isnumber(L,1)) pilot_target = (int)lua_tonumber(L,1);
+  return 0;
+}
+
 // Pew pew.. Says the pilot.
 static int ai_shoot(lua_State* L) {
   int n = 1;
   if(lua_isnumber(L, 1)) n = (int)lua_tonumber(L,1);
 
-  if(n == 1) pilot_primary = 1;
-  //else if(n == 2) pilot_secondary = 1;
-  //else if(n == 3) pilot_primary = pilot_secondary = 1;
+  if(n == 1) ai_setFlag(AI_PRIMARY);
+  else if(n == 2) ai_setFlag(AI_SECONDARY);
+  else if(n == 3) ai_setFlag(AI_PRIMARY | AI_SECONDARY);
 
   return 0;
 }
diff --git a/src/outfit.c b/src/outfit.c
index 14b8fbd..edbc24a 100644
--- a/src/outfit.c
+++ b/src/outfit.c
@@ -7,6 +7,8 @@
 #include "xml.h"
 #include "outfit.h"
 
+#define outfit_setProp(o,p) ((o)->properties |= p)
+
 #define XML_OUTFIT_ID   "Outfits"
 #define XML_OUTFIT_TAG  "outfit"
 
@@ -18,6 +20,8 @@ static int outfits = 0;
 
 static Outfit* outfit_parse(const xmlNodePtr parent);
 static void outfit_parseSWeapon(Outfit* tmp, const xmlNodePtr parent);
+static void outfit_parseSLauncher(Outfit* tmp, const xmlNodePtr parent);
+static void outfit_parseSAmmo(Outfit* tmp, const xmlNodePtr parent);
 
 // Return an outfit.
 Outfit* outfit_get(const char* name) {
@@ -98,7 +102,8 @@ static void outfit_parseSWeapon(Outfit* tmp, const xmlNodePtr parent) {
     }
   } while((node = node->next));
 #define MELEMENT(o,s) if((o) == 0) WARN("Outfit '%s' missing '"s"' element", tmp->name)
-  MELEMENT(tmp->accuracy,       "tech");
+  if(tmp->gfx_space == NULL)
+    WARN("Outfit '%s' missing 'gfx' element", tmp->name);
   MELEMENT(tmp->delay,          "delay");
   MELEMENT(tmp->speed,          "speed");
   MELEMENT(tmp->range,          "range");
@@ -108,6 +113,60 @@ static void outfit_parseSWeapon(Outfit* tmp, const xmlNodePtr parent) {
 #undef MELEMENT
 }
 
+// Parse the specific area for a launcher and loads it into Outfit.
+static void outfit_parseSLauncher(Outfit* tmp, const xmlNodePtr parent) {
+  xmlNodePtr node;
+  node = parent->xmlChildrenNode;
+
+  do {
+    // Load the dataz.
+    if(xml_isNode(node, "delay")) tmp->delay = xml_getInt(node);
+    else if(xml_isNode(node, "ammo")) tmp->ammo = strdup(xml_get(node));
+  } while((node = node->next));
+
+#define MELEMENT(o,s) if((o) == 0) WARN("Outfit '%s' missing '"s"' element", tmp->name)
+  if(tmp->ammo == NULL) WARN("Outfit '%s' missing 'ammo' element", tmp->name);
+  MELEMENT(tmp->delay, "delay");
+#undef MELEMENT
+}
+
+// Parse the specific area for a weapon and load it into Outfit.
+static void outfit_parseSAmmo(Outfit* tmp, const xmlNodePtr parent) {
+  xmlNodePtr cur, node;
+  node = parent->xmlChildrenNode;
+
+  char str[PATH_MAX] = "\0";
+
+  do {
+    if(xml_isNode(node, "thrust")) tmp->thrust = xml_getFloat(node);
+    else if(xml_isNode(node, "turn")) tmp->turn = xml_getFloat(node);
+    else if(xml_isNode(node, "speed")) tmp->speed = xml_getFloat(node);
+    else if(xml_isNode(node, "duration")) tmp->duration = 1000*(unsigned int)xml_getFloat(node);
+    else if(xml_isNode(node, "gfx")) {
+      snprintf(str, strlen(xml_get(node))+sizeof(OUTFIT_GFX)+4,
+            OUTFIT_GFX"%s.png", xml_get(node));
+      tmp->gfx_space = gl_newSprite(str, 6, 6);
+    }
+    else if(xml_isNode(node, "damage")) {
+      cur = node->children;
+      do {
+        if(xml_isNode(cur, "armour")) tmp->damage_armour = xml_getFloat(cur);
+        else if(xml_isNode(cur, "shield")) tmp->damage_shield = xml_getFloat(cur);
+      } while((cur = cur->next));
+    }
+  } while((node = node->next));
+#define MELEMENT(o,s) if((o) == 0) WARN("Outfit '%s' missing '"s"' element", tmp->name)
+  if(tmp->gfx_space == NULL)
+    WARN("Outfit '%s' missing 'gfx' element", tmp->name);
+  MELEMENT(tmp->thrust,   "     thrust");
+  MELEMENT(tmp->turn,           "turn");
+  MELEMENT(tmp->speed,          "speed");
+  MELEMENT(tmp->range,          "duration");
+  MELEMENT(tmp->damage_armour,  "armour' from element 'damage");
+  MELEMENT(tmp->damage_shield,  "shield' from element 'damage");
+#undef MELEMENT
+}
+
 // Parse and return Outfits from parent node.
 static Outfit* outfit_parse(const xmlNodePtr parent) {
   Outfit* tmp = CALLOC_L(Outfit);
@@ -130,26 +189,33 @@ static Outfit* outfit_parse(const xmlNodePtr parent) {
     }
     else if(xml_isNode(node, "specific")) {
       // Has to be processed seperately.
+
+      // Get the type.
       prop = xml_nodeProp(node, "type");
       if(prop == NULL)
         ERR("Outfit '%s' element 'specific' missing property 'type'", tmp->name);
       tmp->type = atoi(prop);
       free(prop);
-      switch(tmp->type) {
-        case OUTFIT_TYPE_NULL:
-          WARN("Outfit '%s' is of type NONE", tmp->name);
-          break;
-        case OUTFIT_TYPE_BOLT:
-          outfit_parseSWeapon(tmp, node);
-          break;
-        default:
-          break;
+
+      // Is this the secondary weapon?
+      prop = xml_nodeProp(node, "secondary");
+      if(prop != NULL) {
+        if((int)atoi(prop)) outfit_setProp(tmp, OUTFIT_PROP_WEAP_SECONDARY);
+        free(prop);
       }
+      if(tmp->type == OUTFIT_TYPE_NULL)
+        WARN("Outfit '%s' is of type NONE", tmp->name);
+      else if(outfit_isWeapon(tmp))
+        outfit_parseSWeapon(tmp, node);
+      else if(outfit_isLauncher(tmp))
+        outfit_parseSLauncher(tmp, node);
+      else if(outfit_isAmmo(tmp))
+        outfit_parseSAmmo(tmp, node);
     }
   } while((node = node->next));
 #define MELEMENT(o,s) if((o) == 0) WARN("Outfit '%s' missing '"s"' element", tmp->name)
   MELEMENT(tmp->max,  "max");
-  MELEMENT(tmp->tech,  "tech");
+  MELEMENT(tmp->tech, "tech");
   MELEMENT(tmp->mass, "mass");
   MELEMENT(tmp->type, "type");
 #undef MELEMENT
@@ -202,6 +268,10 @@ void outfit_free(void) {
   for(i = 0; i < outfits; i++) {
     if(outfit_isWeapon(&outfit_stack[i]) && outfit_stack[i].gfx_space)
       gl_freeTexture(outfit_stack[i].gfx_space);
+
+    if(outfit_isLauncher(&outfit_stack[i]) && outfit_stack[i].ammo)
+      free(outfit_stack[i].ammo);
+
     free(outfit_stack[i].name);
   }
   free(outfit_stack);
diff --git a/src/outfit.h b/src/outfit.h
index d1f93aa..b6c5038 100644
--- a/src/outfit.h
+++ b/src/outfit.h
@@ -1,8 +1,9 @@
 #pragma once
 #include "opengl.h"
 
-#define OUTFIT_PROP_WEAP_PRIMARY    (1<<0)
-#define OUTFIT_PROP_WEAP_SECONDARY  (1<<1)
+#define outfit_isProp(o,p)  ((o)->properties & p)
+// Property flags.
+#define OUTFIT_PROP_WEAP_SECONDARY (1<<0)
 
 // Outfit types.
 typedef enum {
@@ -48,12 +49,14 @@ typedef struct {
     };
     struct { // Launcher.
       //unsigned int delay; // Delay between shots.
+      char* ammo;
     };
     struct { // Ammo.
       //double speed; // Max speed.
-      //double turn; // Turn vel.
-      //double thrust; // Acceleration.
-      //double damage_armour, damage_shield;
+      double turn; // Turn vel.
+      double thrust; // Acceleration.
+      unsigned int duration; // Duration.
+      //double damage_armour, damage_shield; // Damage.
 
       //gl_texture* gfx_space;
     };
diff --git a/src/pilot.c b/src/pilot.c
index fc48b95..15553ff 100644
--- a/src/pilot.c
+++ b/src/pilot.c
@@ -64,7 +64,7 @@ unsigned int pilot_getNearest(const Pilot* p) {
   for(tp = 0, d = 0., i = 0; i < pilots; i++)
     if(areEnemies(p->faction, pilot_stack[i]->faction)) {
       td = vect_dist(&pilot_stack[i]->solid->pos, &p->solid->pos);
-      if((!tp) || (td < d)) {
+      if(!pilot_isDisabled(pilot_stack[i]) && ((!tp) || (td < d))) {
         d = td;
         tp = pilot_stack[i]->id;
       }
@@ -106,27 +106,40 @@ Pilot* pilot_get(const unsigned int id) {
 }
 
 // Mkay, this is how we shoot. Listen up.
-void pilot_shoot(Pilot* p, const int secondary) {
+void pilot_shoot(Pilot* p, const unsigned int target, const int secondary) {
   int i;
   if(!secondary) {
     // Primary weapons.
     if(!p->outfits) return; // No outfits.
 
     for(i = 0; i < p->noutfits; i++) // Cycle through outfits to find weapons.
-      if(outfit_isWeapon(p->outfits[i].outfit) || // Is a weapon or launch?
-            outfit_isLauncher(p->outfits[i].outfit))
+      if(outfit_isWeapon(p->outfits[i].outfit)) // Are we a weapon?
         // Ready to shoot again.
         if((SDL_GetTicks()-p->outfits[i].timer) > (p->outfits[i].outfit->delay/p->outfits[i].quantity))
           // Different weapons have different behaviours.
           switch(p->outfits[i].outfit->type) {
             case OUTFIT_TYPE_BOLT:
               weapon_add(p->outfits[i].outfit, p->solid->dir, &p->solid->pos,
-                    &p->solid->vel, p->id, (p==player) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG);
-              p->outfits[i].timer = SDL_GetTicks();
+                    &p->solid->vel, p->id, target, (p==player) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG);
+              p->outfits[i].timer = SDL_GetTicks(); // Let's not try this again for a while.
               break;
             default:
               break;
           }
+  } else {
+    if(!p->secondary) return; // No secondary weapon.
+
+    if(outfit_isLauncher(p->secondary->outfit)) {
+      if(((SDL_GetTicks()-p->secondary->timer) > 
+            (p->secondary->outfit->delay/p->secondary->quantity)) && 
+            p->ammo && (p->ammo->quantity > 0)) {
+        weapon_add(p->ammo->outfit, p->solid->dir, &p->solid->pos, &p->solid->vel,
+              p->id, target, (p==player) ? WEAPON_LAYER_FG : WEAPON_LAYER_BG);
+
+        p->secondary->timer = SDL_GetTicks(); // Let's not try this again for a while.
+        p->ammo->quantity -= 1; // No getting this one back.
+      }
+    }
   }
 }
 
@@ -145,6 +158,27 @@ void pilot_hit(Pilot* p, const double damage_shield, const double damage_armour)
     p->armour = 0.;
 }
 
+// Set the pilot's ammo based on their secondary weapon.
+void pilot_setAmmo(Pilot* p) {
+  int i;
+  char* name;
+
+  if((p->secondary == NULL) || !outfit_isLauncher(p->secondary->outfit)) {
+    p->ammo = NULL;
+    return;
+  }
+
+  name = p->secondary->outfit->ammo;
+
+  for(i = 0; i < p->noutfits; i++)
+    if(strcmp(p->outfits[i].outfit->name, name)==0) {
+      p->ammo = p->outfits + i;
+      return;
+    }
+
+  p->ammo = NULL;
+}
+
 // Render the pilot.
 void pilot_render(Pilot* p) {
   int sx, sy;
@@ -231,6 +265,8 @@ void pilot_init(Pilot* pilot, Ship* ship, char* name, Faction* faction, AI_Profi
 
   // Outfits.
   pilot->outfits = NULL;
+  pilot->secondary = NULL;
+  pilot->ammo = NULL;
   ShipOutfit* so;
   if(ship->outfit) {
     pilot->noutfits = 0;
@@ -316,7 +352,7 @@ void pilots_free(void) {
 void pilots_update(double dt) {
   int i;
   for(i = 0; i < pilots; i++) {
-    if(pilot_stack[i]->think && !pilot_isFlag(pilot_stack[i], PILOT_DISABLED))
+    if(pilot_stack[i]->think && !pilot_isDisabled(pilot_stack[i]))
       pilot_stack[i]->think(pilot_stack[i]);
     if(pilot_stack[i]->update)
       pilot_stack[i]->update(pilot_stack[i], dt);
diff --git a/src/pilot.h b/src/pilot.h
index 10c6c8e..dc3407d 100644
--- a/src/pilot.h
+++ b/src/pilot.h
@@ -25,6 +25,7 @@
 #define PILOT_DISABLED      (1<<4)  // Pilot is disabled.
 // Just makes life simpler.
 #define pilot_isPlayer(p)   ((p)->flags & PILOT_PLAYER)
+#define pilot_isDisabled(p) ((p)->flags & PILOT_DISABLED)
 
 typedef struct {
   Outfit* outfit; // Associated outfit.
@@ -54,6 +55,8 @@ typedef struct Pilot {
   // Outfit management.
   PilotOutfit* outfits;
   int noutfits;
+  PilotOutfit* secondary; // Secondary weapon.
+  PilotOutfit* ammo;      // Secondary ammo (if needed).
 
   unsigned int flags; // Used for AI etc.
 
@@ -90,8 +93,9 @@ unsigned int pilot_getHostile(void); // Only for the player.
 Fleet* fleet_get(const char* name);
 
 // MISC.
-void pilot_shoot(Pilot* p, const int secondary);
+void pilot_shoot(Pilot* p, const unsigned int target, const int secondary);
 void pilot_hit(Pilot* p, const double damage_shield, const double damage_armour);
+void pilot_setAmmo(Pilot* p);
 
 // Creation.
 void pilot_init(Pilot* dest, Ship* ship, char* name, Faction* faction, AI_Profile* ai,
diff --git a/src/player.c b/src/player.c
index 9d66fea..827d626 100644
--- a/src/player.c
+++ b/src/player.c
@@ -26,6 +26,8 @@
 #define PLAYER_TURN_LEFT  (1<<0)  // Player is turning left.
 #define PLAYER_TURN_RIGHT (1<<1)  // Player is turning right.
 #define PLAYER_FACE       (1<<2)  // Player is facing target.
+#define PLAYER_PRIMARY    (1<<3)  // Player is shooting primary weapon.
+#define PLAYER_SECONDARY  (1<<4)  // Player is shooting secondary weapon.
 
 // Flag functions.
 #define player_isFlag(f)  (player_flags  & f)
@@ -46,19 +48,17 @@ static Keybind** player_input; // Contains the players keybindings.
 // Name of each keybinding.
 const char* keybindNames[] = { "accel", "left", "right", // Movement.
                               "primary", "target", "target_nearest", "face", "board", // Combat.
+                              "secondary", "secondary_next", // Secondary weapons.
                               "mapzoomin", "mapzoomout", "screenshot", "end" }; // Misc.
 
 // Player stuff.
 Pilot* player = NULL; // extern in pilot.h
 unsigned int credits = 0;
-static int player_flags = 0; // Player flags.
+static unsigned int player_flags = 0; // Player flags.
 static double player_turn = 0.; // Turn velocity from input.
 static double player_acc  = 0.; // Accel velocity from input.
-static int player_primary = 0; // Player is shooting primary weapon.
-//static int player_secondary = 0; // layer is shooting secondary weapon.
 static unsigned int player_target = PLAYER_ID; // Targetted pilot.
 static int planet_target = -1; // Targetted planet.
-static int weapon = -1;       // Secondary weapon is in use.
 
 // Pilot stuff for GUI.
 extern Pilot** pilot_stack;
@@ -148,6 +148,7 @@ static void gui_renderBar(const glColour* c, const Vec2* p, const Rect* r, const
 
 // Keybinds.
 static void player_board(void);
+static void player_secondaryNext(void);
 static void player_screenshot(void);
 
 // Create a new player.
@@ -254,7 +255,7 @@ void player_render(void) {
   if(player_target != PLAYER_ID) {
     p = pilot_get(player_target);
 
-    if(pilot_isFlag(p, PILOT_DISABLED)) c = &cInert;
+    if(pilot_isDisabled(p)) c = &cInert;
     else if(pilot_isFlag(p, PILOT_HOSTILE)) c = &cHostile;
     else c = &cNeutral;
 
@@ -340,7 +341,7 @@ void player_render(void) {
   gui_renderBar(&cEnergy, &gui.pos_energy, &gui.energy, player->energy / player->energy_max);
   
   // Weapon.
-  if(weapon == -1) {
+  if(player->secondary == NULL) {
     i = gl_printWidth(NULL, "Secondary");
     vect_csetmin(&v, VX(gui.pos_weapon) + (gui.weapon.w - i)/2., VY(gui.pos_weapon) - 5);
     gl_print(NULL, &v, &cConsole, "Secondary");
@@ -348,7 +349,21 @@ void player_render(void) {
     vect_csetmin(&v, VX(gui.pos_weapon) + (gui.weapon.w - i)/2., VY(gui.pos_weapon) - 10 - gl_defFont.h);
     gl_print(&gui.smallFont, &v, &cGrey, "None");
   } else {
-  
+    if(player->ammo == NULL) {
+      i = gl_printWidth(&gui.smallFont, "%s", player->secondary->outfit->name);
+      vect_csetmin(&v, VX(gui.pos_weapon) + (gui.weapon.w - i)/2.,
+            VY(gui.pos_weapon) - (gui.weapon.h - gui.smallFont.h)/2.);
+      gl_print(&gui.smallFont, &v, &cConsole, "%s", player->secondary->outfit->name);
+    } else {
+      i = gl_printWidth(&gui.smallFont, "%s", player->secondary->outfit->name);
+      vect_csetmin(&v, VX(gui.pos_weapon) + (gui.weapon.w - i)/2.,
+            VY(gui.pos_weapon) - 5);
+      gl_print(&gui.smallFont, &v, NULL, "%s", player->ammo->outfit->name);
+      i = gl_printWidth(NULL, "%d", player->ammo->quantity);
+      vect_csetmin(&v, VX(gui.pos_weapon) + (gui.weapon.w - i)/2.,
+            VY(gui.pos_weapon) - 10 - gl_defFont.h);
+      gl_print(NULL, &v, &cConsole, "%d", player->ammo->quantity);
+    }
   }
 
   // Target.
@@ -362,7 +377,7 @@ void player_render(void) {
     gl_print(&gui.smallFont, &gui.pos_target_faction, NULL, "%s", p->faction->name);
 
     // Target status.
-    if(pilot_isFlag(p, PILOT_DISABLED))
+    if(pilot_isDisabled(p))
       // Disable the pilot.
       gl_print(&gui.smallFont, &gui.pos_target_health, NULL, "Disabled");
     else if(p->shield > p->shield_max / 100.)
@@ -433,7 +448,7 @@ static void gui_renderPilot(const Pilot* p) {
   glBegin(GL_QUADS);
     // Colours.
     if(p->id == player_target) COLOUR(cRadar_targ);
-    else if(pilot_isFlag(p, PILOT_DISABLED)) COLOUR(cInert);
+    else if(pilot_isDisabled(p)) COLOUR(cInert);
     else if(pilot_isFlag(p, PILOT_HOSTILE)) COLOUR(cHostile);
     else COLOUR(cNeutral);
 
@@ -750,7 +765,9 @@ void player_think(Pilot* player) {
   if(player_turn)
     player->solid->dir_vel -= player->ship->turn * player_turn;
 
-  if(player_primary) pilot_shoot(player, 0);
+  if(player_isFlag(PLAYER_PRIMARY)) pilot_shoot(player, 0, 0);
+  if(player_isFlag(PLAYER_SECONDARY) && (player_target != PLAYER_ID)) // Needs a target.
+    pilot_shoot(player, player_target, 1);
 
   vect_pset(&player->solid->force, player->ship->thrust * player_acc, player->solid->dir);
 }
@@ -764,7 +781,7 @@ void player_board(void) {
   }
   p = pilot_get(player_target);
 
-  if(!pilot_isFlag(p, PILOT_DISABLED)) {
+  if(!pilot_isDisabled(p)) {
     player_message("You cannot board a ship that isn't disabled!");
     return;
   }
@@ -778,7 +795,32 @@ void player_board(void) {
     player_message("You are going too fact to board the ship");
     return;
   }
-  player_message("Ship boarding isn't implemented yet! HAHA");
+  player_message("It's a shame Allanis hasn't added boarding yet, right?!");
+}
+
+// Get the next secondary weapon.
+static void player_secondaryNext(void) {
+  int i = 0;
+
+  // Get the current secondary weapon pos.
+  if(player->secondary != NULL)
+    for(i = 0; i < player->noutfits; i++)
+      if(&player->outfits[i] == player->secondary) {
+        i++;
+        break;
+      }
+  // Get the next secondary weapon.
+  for(; i < player->noutfits; i++)
+    if(outfit_isProp(player->outfits[i].outfit, OUTFIT_PROP_WEAP_SECONDARY)) {
+      player->secondary = player->outfits + i;
+      break;
+    }
+  // We didn't find an outfit.
+  if(i >= player->noutfits)
+    player->secondary = NULL;
+
+  // Set ammo.
+  pilot_setAmmo(player);
 }
 
 // Take a screenshot.
@@ -795,17 +837,19 @@ static void player_screenshot(void) {
 
 // Set the default input keys.
 void input_setDefault(void) {
-  input_setKeybind("accel",          KEYBIND_KEYBOARD, SDLK_w,     0);
-  input_setKeybind("left",           KEYBIND_KEYBOARD, SDLK_a,     0);
-  input_setKeybind("right",          KEYBIND_KEYBOARD, SDLK_d,     0);
-  input_setKeybind("primary",        KEYBIND_KEYBOARD, SDLK_SPACE, 0);
-  input_setKeybind("target",         KEYBIND_KEYBOARD, SDLK_TAB,   0);
-  input_setKeybind("target_nearest", KEYBIND_KEYBOARD, SDLK_r,     0);
-  input_setKeybind("face",           KEYBIND_KEYBOARD, SDLK_f,     0);
-  input_setKeybind("board",          KEYBIND_KEYBOARD, SDLK_b,     0);
-  input_setKeybind("mapzoomin",      KEYBIND_KEYBOARD, SDLK_UP,    0);
-  input_setKeybind("mapzoomout",     KEYBIND_KEYBOARD, SDLK_DOWN,  0);
-  input_setKeybind("screenshot",     KEYBIND_KEYBOARD, SDLK_F12,   0);
+  input_setKeybind("accel",           KEYBIND_KEYBOARD, SDLK_w,       0);
+  input_setKeybind("left",            KEYBIND_KEYBOARD, SDLK_a,       0);
+  input_setKeybind("right",           KEYBIND_KEYBOARD, SDLK_d,       0);
+  input_setKeybind("primary",         KEYBIND_KEYBOARD, SDLK_SPACE,   0);
+  input_setKeybind("target",          KEYBIND_KEYBOARD, SDLK_TAB,     0);
+  input_setKeybind("target_nearest",  KEYBIND_KEYBOARD, SDLK_r,       0);
+  input_setKeybind("face",            KEYBIND_KEYBOARD, SDLK_f,       0);
+  input_setKeybind("board",           KEYBIND_KEYBOARD, SDLK_b,       0);
+  input_setKeybind("secondary",       KEYBIND_KEYBOARD, SDLK_LSHIFT,  0);
+  input_setKeybind("secondary_next",  KEYBIND_KEYBOARD, SDLK_q,       0);
+  input_setKeybind("mapzoomin",       KEYBIND_KEYBOARD, SDLK_UP,      0);
+  input_setKeybind("mapzoomout",      KEYBIND_KEYBOARD, SDLK_DOWN,    0);
+  input_setKeybind("screenshot",      KEYBIND_KEYBOARD, SDLK_F12,     0);
 }
 
 // Initialization/exit functions (does not assign keys).
@@ -881,8 +925,8 @@ static void input_key(int keynum, double value, int abs) {
   }
   // Shoot primary weapon. BOOM BOOM.
   else if(strcmp(player_input[keynum]->name, "primary")==0) {
-    if(value == KEY_PRESS) player_primary = 1;
-    else if(value == KEY_RELEASE) player_primary = 0;
+    if(value == KEY_PRESS) player_setFlag(PLAYER_PRIMARY);
+    else if(value == KEY_RELEASE) player_rmFlag(PLAYER_PRIMARY);
   }
   // Targetting.
   else if(strcmp(player_input[keynum]->name, "target")==0) {
@@ -907,16 +951,26 @@ static void input_key(int keynum, double value, int abs) {
   else if(strcmp(player_input[keynum]->name, "board")==0) {
     if(value == KEY_PRESS) player_board();
   }
+  // Shooting secondary weapon.
+  else if(strcmp(player_input[keynum]->name, "secondary")==0) {
+    if(value == KEY_PRESS) player_setFlag(PLAYER_SECONDARY);
+    else if(value == KEY_RELEASE) player_rmFlag(PLAYER_SECONDARY);
+  }
+  // Selecting secondary weapon.
+  else if(strcmp(player_input[keynum]->name, "secondary_next")==0) {
+    if(value == KEY_PRESS) player_secondaryNext();
+  }
   // Zoom in.
   else if(strcmp(player_input[keynum]->name, "mapzoomin")==0) {
-    if(value == KEY_PRESS && gui.radar.res < RADAR_RES_MAX)
+    if((value == KEY_PRESS) && (gui.radar.res < RADAR_RES_MAX))
       gui.radar.res += RADAR_RES_INTERVAL;
   }
   // Zoom out.
   else if(strcmp(player_input[keynum]->name, "mapzoomout")==0) {
-    if(value == KEY_PRESS && gui.radar.res > RADAR_RES_MIN)
+    if((value == KEY_PRESS) && (gui.radar.res > RADAR_RES_MIN))
       gui.radar.res -= RADAR_RES_INTERVAL;
   }
+  // Take a screenshot.
   else if(strcmp(player_input[keynum]->name, "screenshot")==0) {
     if(value == KEY_PRESS) player_screenshot();
   }
diff --git a/src/ship.c b/src/ship.c
index 0ee4738..c482317 100644
--- a/src/ship.c
+++ b/src/ship.c
@@ -117,7 +117,7 @@ static Ship* ship_parse(xmlNodePtr parent) {
 
           if((ocur = tmp->outfit) == NULL) tmp->outfit = otmp;
           else {
-            while(ocur->next);
+            while(ocur->next) ocur = ocur->next;
             ocur->next = otmp;
           }
         }
diff --git a/src/weapon.c b/src/weapon.c
index bea42cf..2520fa0 100644
--- a/src/weapon.c
+++ b/src/weapon.c
@@ -12,6 +12,8 @@
 #include "player.h"
 #include "weapon.h"
 
+#define weapon_isSmart(w) (w->think)
+
 // Some stuff from pilot.
 extern Pilot** pilot_stack;
 extern int pilots;
@@ -23,6 +25,7 @@ typedef struct Weapon {
   Solid* solid; // Actually has its own solid. :D
 
   unsigned int parent; // The pilot that just shot at you!
+  unsigned int target; // Target to hit. Only used by seeking stuff.
   const Outfit* outfit; // Related outfit that fired.
   unsigned int timer; // Mainly used to see when the weapon was fired.
 
@@ -42,12 +45,16 @@ static int mwfrontLayer    = 0;    // Allocated memory size.
 
 
 static Weapon* weapon_create(const Outfit* outfit, const double dir,
-      const Vec2* pos, const Vec2* vel, unsigned int parent);
+      const Vec2* pos, const Vec2* vel, const unsigned int parent, 
+      const unsigned int target);
 
 static void weapon_render(const Weapon* w);
 static void weapon_update(Weapon* w, const double dt, WeaponLayer layer);
+static void weapon_hit(Weapon* w, Pilot* p, WeaponLayer layer);
 static void weapon_destroy(Weapon* w, WeaponLayer layer);
 static void weapon_free(Weapon* w);
+// Think.
+static void think_seeker(Weapon* w);
 
 // Draw the minimap weapons (player.c).
 #define PIXEL(x,y) if((shape == RADAR_RECT && ABS(x) < w/2. && ABS(y)<h/2.) || \
@@ -72,6 +79,25 @@ void weapon_minimap(const double res, const double w, const double h, const Rada
 }
 #undef PIXEL
 
+static void think_seeker(Weapon* w) {
+  if(w->target == w->parent) return; // HEY! Self harm is not allowed.
+
+  Pilot* p = pilot_get(w->target);
+  if(p == NULL) return; // Can't do anything with no pilots.
+
+  double diff = angle_diff(w->solid->dir, vect_angle(&w->solid->pos, &p->solid->pos));
+  w->solid->dir_vel = 10 * diff * w->outfit->turn;
+  // Face the target.
+  if(w->solid->dir_vel > w->outfit->turn) w->solid->dir_vel = w->outfit->turn;
+  else if(w->solid->dir_vel < -w->outfit->turn) w->solid->dir_vel = -w->outfit->turn;
+
+  vect_pset(&w->solid->force, w->outfit->thrust, w->solid->dir);
+
+  if(VMOD(w->solid->vel) > w->outfit->speed)
+    // We should not go any faster.
+    vect_pset(&w->solid->vel, w->outfit->speed, VANGLE(w->solid->vel));
+}
+
 // Update all weapons in the layer.
 void weapons_update(const double dt, WeaponLayer layer) {
   Weapon** wlayer;
@@ -92,15 +118,20 @@ void weapons_update(const double dt, WeaponLayer layer) {
   for(i = 0; i < (*nlayer); i++) {
     w = wlayer[i];
     switch(wlayer[i]->outfit->type) {
-      case OUTFIT_TYPE_BOLT:
+      case OUTFIT_TYPE_MISSILE_SEEK_AMMO:
+        if(SDL_GetTicks() > (wlayer[i]->timer + wlayer[i]->outfit->duration)) {
+          weapon_destroy(wlayer[i], layer);
+          continue;
+        }
+        break;
+      default:
+        // Check see if it exceeds distance.
         if(SDL_GetTicks() > (wlayer[i]->timer + 1000*(unsigned int)
               wlayer[i]->outfit->range/wlayer[i]->outfit->speed)) {
           weapon_destroy(wlayer[i],layer);
           continue;
         }
         break;
-      default:
-        break;
     }
     weapon_update(wlayer[i], dt, layer);
     // If the weapon has been deleted we are going to have to hold back one.
@@ -126,38 +157,51 @@ static void weapon_update(Weapon* w, const double dt, WeaponLayer layer) {
   for(i = 0; i < pilots; i++) {
     gl_getSpriteFromDir(&psx, &psy, pilot_stack[i]->ship->gfx_space,
           pilot_stack[i]->solid->dir);
+    if(w->parent == pilot_stack[i]->id) continue; // Hey! That's you.
 
-    if((w->parent != pilot_stack[i]->id) && // The pilot hasn't shoot it.
+    if((weapon_isSmart(w)) && (pilot_stack[i]->id == w->target) &&
+          CollideSprite(w->outfit->gfx_space, wsx, wsy, &w->solid->pos,
+                pilot_stack[i]->ship->gfx_space, psx, psy, &pilot_stack[i]->solid->pos)) {
+      weapon_hit(w, pilot_stack[i], layer);
+      return;
+    }
+    else if(!weapon_isSmart(w) &&
           !areAllies(pilot_get(w->parent)->faction, pilot_stack[i]->faction) &&
           CollideSprite(w->outfit->gfx_space, wsx, wsy, &w->solid->pos,
-          pilot_stack[i]->ship->gfx_space, psx, psy, &pilot_stack[i]->solid->pos)) {
-
-      if(!pilot_isPlayer(pilot_stack[i]))
-        // Inform the ai it has been attacked. Useless if we are player.
-        ai_attacked(pilot_stack[i], w->parent);
-      if(w->parent == PLAYER_ID) // Make hostile to player.
-        pilot_setFlag(pilot_stack[i], PILOT_HOSTILE);
-
-      // Inform the ship that it should take some damage.
-      pilot_hit(pilot_stack[i], w->outfit->damage_shield, w->outfit->damage_armour);
-      // No need for the weapon particle anymore.
-      weapon_destroy(w, layer);
+                pilot_stack[i]->ship->gfx_space, psx, psy, &pilot_stack[i]->solid->pos)) {
+      weapon_hit(w, pilot_stack[i], layer);
       return;
     }
   }
-  if(w->think) (*w->think)(w);
+  if(weapon_isSmart(w)) (*w->think)(w);
   (*w->solid->update)(w->solid, dt);
 
   weapon_render(w);
 }
 
+// Good shot.
+static void weapon_hit(Weapon* w, Pilot* p, WeaponLayer layer) {
+  // Someone should let the ai know it's been attacked.
+  if(!pilot_isPlayer(p))
+    ai_attacked(p, w->parent);
+  if(w->parent == PLAYER_ID)
+    // Make hostile to player.
+    pilot_setFlag(p, PILOT_HOSTILE);
+
+  // Let the ship know that is should take some kind of damage.
+  pilot_hit(p, w->outfit->damage_shield, w->outfit->damage_armour);
+  // We don't need the weapon particle any longer.
+  weapon_destroy(w, layer);
+}
+
 static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* pos,
-      const Vec2* vel, unsigned int parent) {
+      const Vec2* vel, unsigned int parent, const unsigned int target) {
   Vec2 v;
   double mass = 1; // Presumer lasers have a mass of 1.
   double rdir = dir; // Real direction (accuracy).
   Weapon* w = MALLOC_L(Weapon);
   w->parent = parent; // Non-Changeable.
+  w->target = target; // Non-Changeable.
   w->outfit = outfit; // Non-Changeable.
   w->update = weapon_update;
   w->timer = SDL_GetTicks();
@@ -172,6 +216,11 @@ static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2*
       vect_cadd(&v, outfit->speed*cos(rdir), outfit->speed*sin(rdir));
       w->solid = solid_create(mass, rdir, pos, &v);
       break;
+    case OUTFIT_TYPE_MISSILE_SEEK_AMMO:
+      mass = w->outfit->mass;
+      w->solid = solid_create(mass, dir, pos, vel);
+      w->think = think_seeker; // Eeek!!!
+      break;
     default:
       // Just dump it where the player is.
       w->solid = solid_create(mass, dir, pos, vel);
@@ -182,13 +231,14 @@ static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2*
 
 // Add a new weapon.
 void weapon_add(const Outfit* outfit, const double dir, const Vec2* pos, const Vec2* vel,
-      unsigned int parent, WeaponLayer layer) {
-  if(!outfit_isWeapon(outfit)) {
+      unsigned int parent, unsigned int target,  WeaponLayer layer) {
+  
+  if(!outfit_isWeapon(outfit) && !outfit_isAmmo(outfit)) {
     ERR("Trying to create a weapon from a non-Weapon type Outfit");
     return;
   }
 
-  Weapon* w = weapon_create(outfit, dir, pos, vel, parent);
+  Weapon* w = weapon_create(outfit, dir, pos, vel, parent, target);
 
   // Set the propper layer.
   Weapon** curLayer = NULL;
diff --git a/src/weapon.h b/src/weapon.h
index 9fe79a8..f13c16f 100644
--- a/src/weapon.h
+++ b/src/weapon.h
@@ -5,7 +5,8 @@
 typedef enum { WEAPON_LAYER_BG, WEAPON_LAYER_FG } WeaponLayer;
 
 void weapon_add(const Outfit* outfit, const double dir,
-      const Vec2* pos, const Vec2* vel, unsigned int parent, WeaponLayer layer);
+      const Vec2* pos, const Vec2* vel, unsigned int parent,
+      const unsigned int target, WeaponLayer layer);
 
 void weapons_update(const double dt, WeaponLayer layer);