AI.C 134 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829
  1. /*
  2. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  3. SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
  4. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  5. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  6. IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  7. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  8. FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  9. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
  10. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
  11. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
  12. */
  13. /*
  14. * $Source: f:/miner/source/main/rcs/ai.c $
  15. * $Revision: 2.11 $
  16. * $Author: john $
  17. * $Date: 1995/07/09 11:15:48 $
  18. *
  19. * Autonomous Individual movement.
  20. *
  21. * $Log: ai.c $
  22. * Revision 2.11 1995/07/09 11:15:48 john
  23. * Put in Mike's code to fix bug where bosses don't gate in bots after
  24. * 32767 seconds of playing.
  25. *
  26. * Revision 2.10 1995/06/15 12:31:08 john
  27. * Fixed bug with cheats getting enabled when you type
  28. * the whole alphabet.
  29. *
  30. * Revision 2.9 1995/05/26 16:16:18 john
  31. * Split SATURN into define's for requiring cd, using cd, etc.
  32. * Also started adding all the Rockwell stuff.
  33. *
  34. * Revision 2.8 1995/04/06 15:12:27 john
  35. * Fixed bug with insane not working.
  36. *
  37. * Revision 2.7 1995/03/30 16:36:44 mike
  38. * text localization.
  39. *
  40. * Revision 2.6 1995/03/28 11:22:24 john
  41. * Added cheats to save file. Changed lunacy text.
  42. *
  43. * Revision 2.5 1995/03/27 16:45:07 john
  44. * Fixed some cheat bugs. Added astral cheat.
  45. *
  46. * Revision 2.4 1995/03/24 15:29:17 mike
  47. * add new cheats.
  48. *
  49. * Revision 2.3 1995/03/21 14:39:45 john
  50. * Ifdef'd out the NETWORK code.
  51. *
  52. * Revision 2.2 1995/03/14 18:24:39 john
  53. * Force Destination Saturn to use CD-ROM drive.
  54. *
  55. * Revision 2.1 1995/03/06 16:47:14 mike
  56. * destination saturn
  57. *
  58. * Revision 2.0 1995/02/27 11:30:01 john
  59. * New version 2.0, which has no anonymous unions, builds with
  60. * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
  61. *
  62. * Revision 1.295 1995/02/22 13:23:04 allender
  63. * remove anonymous unions from object structure
  64. *
  65. * Revision 1.294 1995/02/13 11:00:43 rob
  66. * Make brain guys high enough to get an open slot.
  67. *
  68. * Revision 1.293 1995/02/13 10:31:55 mike
  69. * Make brains understand they can't open locked doors.
  70. *
  71. * Revision 1.292 1995/02/13 10:18:01 rob
  72. * Reduced brain guy's level of awareness to keep him from hogging slots.
  73. *
  74. * Revision 1.291 1995/02/11 12:27:12 mike
  75. * fix path-to-exit cheat.
  76. *
  77. * Revision 1.290 1995/02/11 01:56:30 mike
  78. * robots don't fire cheat.
  79. *
  80. * Revision 1.289 1995/02/10 17:15:09 rob
  81. * Fixed some stuff with 64 awareness stuff.
  82. *
  83. * Revision 1.288 1995/02/10 16:31:32 mike
  84. * oops.
  85. *
  86. * Revision 1.287 1995/02/10 16:24:45 mike
  87. * fix the network follow path fix.
  88. *
  89. * Revision 1.286 1995/02/10 16:11:40 mike
  90. * in serial or modem games, follow path guys don't move if far away and
  91. * can't see player.
  92. *
  93. * Revision 1.285 1995/02/09 13:11:35 mike
  94. * comment out a bunch of mprintfs.
  95. * add toaster (drops prox bombs, runs away) to boss gate list.
  96. *
  97. * Revision 1.284 1995/02/08 22:44:53 rob
  98. * Lowerd anger level for follow path of any sort.
  99. *
  100. * Revision 1.283 1995/02/08 22:30:43 mike
  101. * lower awareness on station guys if they are returning home (multiplayer).
  102. *
  103. * Revision 1.282 1995/02/08 17:01:06 rob
  104. * Fixed problem with toasters dropping of proximity bombs.
  105. *
  106. * Revision 1.281 1995/02/08 11:49:35 rob
  107. * Reduce Green-guy attack awareness level so we don't let him attack us too.
  108. *
  109. * Revision 1.280 1995/02/08 11:37:52 mike
  110. * Check for failures in call to obj_create.
  111. *
  112. * Revision 1.279 1995/02/07 20:38:46 mike
  113. * fix toasters in multiplayer
  114. *
  115. *
  116. * Revision 1.278 1995/02/07 16:51:07 mike
  117. * fix sound time play bug.
  118. *
  119. * Revision 1.277 1995/02/06 22:33:04 mike
  120. * make robots follow path better in cooperative/roboarchy.
  121. *
  122. * Revision 1.276 1995/02/06 18:15:42 rob
  123. * Added forced sends for evasion movemnet.
  124. *
  125. * Revision 1.275 1995/02/06 16:41:22 rob
  126. * Change some positioning calls.
  127. *
  128. * Revision 1.274 1995/02/06 11:40:33 mike
  129. * replace some lint-related hacks with clean, proper code.
  130. *
  131. * Revision 1.273 1995/02/04 17:28:19 mike
  132. * make station guys return better.
  133. *
  134. * Revision 1.272 1995/02/03 17:40:55 mike
  135. * fix problem with robots falling asleep if you sit in game overnight, not in pause...bah.
  136. *
  137. * Revision 1.271 1995/02/02 21:11:25 rob
  138. * Tweaking stuff for multiplayer ai.
  139. *
  140. * Revision 1.270 1995/02/02 17:32:06 john
  141. * Added Hack for Assert that Mike put in after using Lint to find
  142. * uninitialized variables.
  143. *
  144. * Revision 1.269 1995/02/02 16:46:31 mike
  145. * fix boss gating.
  146. *
  147. * Revision 1.268 1995/02/02 16:27:29 mike
  148. * make boss not put out infinite robots.
  149. *
  150. * Revision 1.267 1995/02/01 21:10:02 mike
  151. * lint found bug! player_visibility not initialized!
  152. *
  153. * Revision 1.266 1995/02/01 20:51:27 john
  154. * Lintized
  155. *
  156. * Revision 1.265 1995/02/01 17:14:05 mike
  157. * fix robot sounds.
  158. *
  159. * Revision 1.264 1995/01/31 16:16:40 mike
  160. * Comment out "Darn you, John" Int3().
  161. *
  162. * Revision 1.263 1995/01/30 20:55:04 mike
  163. * fix nonsense in robot firing when a player is cloaked.
  164. *
  165. * Revision 1.262 1995/01/30 17:15:10 rob
  166. * Fixed problems with bigboss eclip messages.
  167. * Tweaked robot position sending for modem purposes.
  168. *
  169. * Revision 1.261 1995/01/30 15:30:31 rob
  170. * Prevent non-master players from gating in robots.
  171. *
  172. * Revision 1.260 1995/01/30 13:30:55 mike
  173. * new cases for firing at other players were bogus, could send position
  174. * without permission.
  175. *
  176. * Revision 1.259 1995/01/30 13:01:17 mike
  177. * Make robots fire at player other than one they are controlled by sometimes.
  178. *
  179. * Revision 1.258 1995/01/29 16:09:17 rob
  180. * Trying to get robots to shoot at non-controlling players.
  181. *
  182. * Revision 1.257 1995/01/29 13:47:05 mike
  183. * Make boss have more fireballs on death, have until end (though silent at end).
  184. * Fix bug which was preventing him from teleporting until hit, so he'd always
  185. * be in the same place when the player enters the room.
  186. *
  187. * Revision 1.256 1995/01/28 17:40:18 mike
  188. * make boss teleport & gate before you see him.
  189. *
  190. * Revision 1.255 1995/01/27 17:02:08 mike
  191. * move code around, was sending one frame (or worse!) old robot information.
  192. *
  193. * Revision 1.254 1995/01/26 17:02:43 mike
  194. * make fusion cannon have more chrome, make fusion, mega rock you!
  195. *
  196. * Revision 1.253 1995/01/26 15:11:17 rob
  197. * Shutup! I fixed it!
  198. *
  199. * Revision 1.252 1995/01/26 15:08:55 rob
  200. * Changed robot gating to accomodate multiplayer.
  201. *
  202. * Revision 1.251 1995/01/26 14:49:04 rob
  203. * Increase awareness level for firing to 94.
  204. *
  205. * Revision 1.250 1995/01/26 12:41:20 mike
  206. * fix bogus multiplayer code, would send permission without getting permission.
  207. *
  208. * Revision 1.249 1995/01/26 12:23:23 rob
  209. * Removed defines that were moved to ai.h
  210. *
  211. * Revision 1.248 1995/01/25 23:38:48 mike
  212. * modify list of robots gated in by super boss.
  213. *
  214. * Revision 1.247 1995/01/25 21:21:13 rob
  215. * Trying to let robots fire at a player even if they're not in control.
  216. *
  217. * Revision 1.246 1995/01/25 13:50:37 mike
  218. * Robots make angry sounds.
  219. *
  220. * Revision 1.245 1995/01/25 10:53:47 mike
  221. * better handling of robots which poke out of mine and try to recover.
  222. *
  223. * Revision 1.244 1995/01/24 22:03:02 mike
  224. * Tricky code to move a robot to a legal position if he is poking out of
  225. * the mine, even if it means moving him to another segment.
  226. *
  227. * Revision 1.243 1995/01/24 20:12:06 rob
  228. * Changed robot fire awareness level from 74 to 94.
  229. *
  230. * Revision 1.242 1995/01/24 13:22:32 mike
  231. * make robots accelerate faster, and Difficulty_level dependent.
  232. *
  233. * Revision 1.241 1995/01/24 12:09:39 mike
  234. * make robots animate in multiplayer.
  235. *
  236. * Revision 1.240 1995/01/21 21:21:10 mike
  237. * Make boss only gate robots into specified segments.
  238. *
  239. * Revision 1.239 1995/01/20 20:21:26 mike
  240. * prevent unnecessary boss cloaking.
  241. *
  242. */
  243. #pragma off (unreferenced)
  244. static char rcsid[] = "$Id: ai.c 2.11 1995/07/09 11:15:48 john Exp $";
  245. #pragma on (unreferenced)
  246. #include <stdio.h>
  247. #include <stdlib.h>
  248. #include "inferno.h"
  249. #include "game.h"
  250. #include "mono.h"
  251. #include "3d.h"
  252. #include "object.h"
  253. #include "render.h"
  254. #include "error.h"
  255. #include "ai.h"
  256. #include "laser.h"
  257. #include "fvi.h"
  258. #include "polyobj.h"
  259. #include "bm.h"
  260. #include "weapon.h"
  261. #include "physics.h"
  262. #include "collide.h"
  263. #include "fuelcen.h"
  264. #include "player.h"
  265. #include "wall.h"
  266. #include "vclip.h"
  267. #include "digi.h"
  268. #include "fireball.h"
  269. #include "morph.h"
  270. #include "effects.h"
  271. #include "timer.h"
  272. #include "sounds.h"
  273. #include "cntrlcen.h"
  274. #include "multibot.h"
  275. #include "multi.h"
  276. #include "network.h"
  277. #include "gameseq.h"
  278. #include "key.h"
  279. #include "powerup.h"
  280. #include "gauges.h"
  281. #include "text.h"
  282. #ifdef EDITOR
  283. #include "editor\editor.h"
  284. #endif
  285. #ifndef NDEBUG
  286. #include "string.h"
  287. #include <time.h>
  288. #endif
  289. #define JOHN_CHEATS_SIZE_1 6
  290. #define JOHN_CHEATS_SIZE_2 6
  291. #define JOHN_CHEATS_SIZE_3 6
  292. ubyte john_cheats_1[JOHN_CHEATS_SIZE_1] = { KEY_P ^ 0x00 ^ 0x34,
  293. KEY_O ^ 0x10 ^ 0x34,
  294. KEY_B ^ 0x20 ^ 0x34,
  295. KEY_O ^ 0x30 ^ 0x34,
  296. KEY_Y ^ 0x40 ^ 0x34,
  297. KEY_S ^ 0x50 ^ 0x34 };
  298. #define PARALLAX 0 // If !0, then special debugging info for Parallax eyes only enabled.
  299. #define MIN_D 0x100
  300. int Flinch_scale = 4;
  301. int john_cheats_index_1; // POBOYS detonate reactor
  302. int Attack_scale = 24;
  303. #define ANIM_RATE (F1_0/16)
  304. #define DELTA_ANG_SCALE 16
  305. byte Mike_to_matt_xlate[] = {AS_REST, AS_REST, AS_ALERT, AS_ALERT, AS_FLINCH, AS_FIRE, AS_RECOIL, AS_REST};
  306. int john_cheats_index_2; // PORGYS high speed weapon firing
  307. // int No_ai_flag=0;
  308. #define OVERALL_AGITATION_MAX 100
  309. #define MAX_AI_CLOAK_INFO 8 // Must be a power of 2!
  310. typedef struct {
  311. fix last_time;
  312. vms_vector last_position;
  313. } ai_cloak_info;
  314. #define BOSS_CLOAK_DURATION (F1_0*7)
  315. #define BOSS_DEATH_DURATION (F1_0*6)
  316. #define BOSS_DEATH_SOUND_DURATION 0x2ae14 // 2.68 seconds
  317. // Amount of time since the current robot was last processed for things such as movement.
  318. // It is not valid to use FrameTime because robots do not get moved every frame.
  319. //fix AI_proc_time;
  320. int Num_boss_teleport_segs;
  321. short Boss_teleport_segs[MAX_BOSS_TELEPORT_SEGS];
  322. #ifndef SHAREWARE
  323. int Num_boss_gate_segs;
  324. short Boss_gate_segs[MAX_BOSS_TELEPORT_SEGS];
  325. #endif
  326. int john_cheats_index_3; // LUNACY lunacy (insane behavior, rookie firing)
  327. // ---------- John: These variables must be saved as part of gamesave. ----------
  328. int Ai_initialized = 0;
  329. int Overall_agitation;
  330. ai_local Ai_local_info[MAX_OBJECTS];
  331. point_seg Point_segs[MAX_POINT_SEGS];
  332. point_seg *Point_segs_free_ptr = Point_segs;
  333. ai_cloak_info Ai_cloak_info[MAX_AI_CLOAK_INFO];
  334. fix Boss_cloak_start_time = 0;
  335. fix Boss_cloak_end_time = 0;
  336. fix Last_teleport_time = 0;
  337. fix Boss_teleport_interval = F1_0*8;
  338. fix Boss_cloak_interval = F1_0*10; // Time between cloaks
  339. fix Boss_cloak_duration = BOSS_CLOAK_DURATION;
  340. fix Last_gate_time = 0;
  341. fix Gate_interval = F1_0*6;
  342. fix Boss_dying_start_time;
  343. int Boss_dying, Boss_dying_sound_playing, Boss_hit_this_frame;
  344. int Boss_been_hit=0;
  345. // ---------- John: End of variables which must be saved as part of gamesave. ----------
  346. int john_cheats_index_4; // PLETCHnnn paint robots
  347. int ai_evaded=0;
  348. #ifndef SHAREWARE
  349. // 0 mech
  350. // 1 green claw
  351. // 2 spider
  352. // 3 josh
  353. // 4 violet
  354. // 5 cloak vulcan
  355. // 6 cloak mech
  356. // 7 brain
  357. // 8 onearm
  358. // 9 plasma
  359. // 10 toaster
  360. // 11 bird
  361. // 12 missile bird
  362. // 13 polyhedron
  363. // 14 baby spider
  364. // 15 mini boss
  365. // 16 super mech
  366. // 17 shareware boss
  367. // 18 cloak-green ; note, gating in this guy benefits player, cloak objects
  368. // 19 vulcan
  369. // 20 toad
  370. // 21 4-claw
  371. // 22 quad-laser
  372. // 23 super boss
  373. // byte Super_boss_gate_list[] = {0, 1, 2, 9, 11, 16, 18, 19, 21, 22, 0, 9, 9, 16, 16, 18, 19, 19, 22, 22};
  374. byte Super_boss_gate_list[] = {0, 1, 8, 9, 10, 11, 12, 15, 16, 18, 19, 20, 22, 0, 8, 11, 19, 20, 8, 20, 8};
  375. #define MAX_GATE_INDEX ( sizeof(Super_boss_gate_list) / sizeof(Super_boss_gate_list[0]) )
  376. #endif
  377. int Ai_info_enabled=0;
  378. int Robot_firing_enabled = 1;
  379. extern int Ugly_robot_cheat, Ugly_robot_texture, Laser_rapid_fire;
  380. extern byte Enable_john_cheat_1, Enable_john_cheat_2, Enable_john_cheat_3, Enable_john_cheat_4;
  381. ubyte john_cheats_3[2*JOHN_CHEATS_SIZE_3+1] = { KEY_Y ^ 0x67,
  382. KEY_E ^ 0x66,
  383. KEY_C ^ 0x65,
  384. KEY_A ^ 0x64,
  385. KEY_N ^ 0x63,
  386. KEY_U ^ 0x62,
  387. KEY_L ^ 0x61 };
  388. #define MAX_AWARENESS_EVENTS 64
  389. typedef struct awareness_event {
  390. short segnum; // segment the event occurred in
  391. short type; // type of event, defines behavior
  392. vms_vector pos; // absolute 3 space location of event
  393. } awareness_event;
  394. // These globals are set by a call to find_vector_intersection, which is a slow routine,
  395. // so we don't want to call it again (for this object) unless we have to.
  396. vms_vector Hit_pos;
  397. int Hit_type, Hit_seg;
  398. fvi_info Hit_data;
  399. int Num_awareness_events = 0;
  400. awareness_event Awareness_events[MAX_AWARENESS_EVENTS];
  401. vms_vector Believed_player_pos;
  402. #define AIS_MAX 8
  403. #define AIE_MAX 4
  404. //--unused-- int Processed_this_frame, LastFrameCount;
  405. #ifndef NDEBUG
  406. // Index into this array with ailp->mode
  407. char mode_text[8][9] = {
  408. "STILL ",
  409. "WANDER ",
  410. "FOL_PATH",
  411. "CHASE_OB",
  412. "RUN_FROM",
  413. "HIDE ",
  414. "FOL_PAT2",
  415. "OPENDOR2"
  416. };
  417. // Index into this array with aip->behavior
  418. char behavior_text[6][9] = {
  419. "STILL ",
  420. "NORMAL ",
  421. "HIDE ",
  422. "RUN_FROM",
  423. "FOLPATH ",
  424. "STATION "
  425. };
  426. // Index into this array with aip->GOAL_STATE or aip->CURRENT_STATE
  427. char state_text[8][5] = {
  428. "NONE",
  429. "REST",
  430. "SRCH",
  431. "LOCK",
  432. "FLIN",
  433. "FIRE",
  434. "RECO",
  435. "ERR_",
  436. };
  437. int Ai_animation_test=0;
  438. #endif
  439. // Current state indicates where the robot current is, or has just done.
  440. // Transition table between states for an AI object.
  441. // First dimension is trigger event.
  442. // Second dimension is current state.
  443. // Third dimension is goal state.
  444. // Result is new goal state.
  445. // ERR_ means something impossible has happened.
  446. byte Ai_transition_table[AI_MAX_EVENT][AI_MAX_STATE][AI_MAX_STATE] = {
  447. {
  448. // Event = AIE_FIRE, a nearby object fired
  449. // none rest srch lock flin fire reco // CURRENT is rows, GOAL is columns
  450. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO}, // none
  451. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO}, // rest
  452. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO}, // search
  453. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO}, // lock
  454. { AIS_ERR_, AIS_REST, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FIRE, AIS_RECO}, // flinch
  455. { AIS_ERR_, AIS_FIRE, AIS_FIRE, AIS_FIRE, AIS_FLIN, AIS_FIRE, AIS_RECO}, // fire
  456. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_FIRE} // recoil
  457. },
  458. // Event = AIE_HITT, a nearby object was hit (or a wall was hit)
  459. {
  460. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
  461. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
  462. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
  463. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
  464. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FLIN},
  465. { AIS_ERR_, AIS_REST, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FIRE, AIS_RECO},
  466. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_FIRE}
  467. },
  468. // Event = AIE_COLL, player collided with robot
  469. {
  470. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
  471. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
  472. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
  473. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
  474. { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_LOCK, AIS_FLIN, AIS_FLIN},
  475. { AIS_ERR_, AIS_REST, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FIRE, AIS_RECO},
  476. { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_FIRE}
  477. },
  478. // Event = AIE_HURT, player hurt robot (by firing at and hitting it)
  479. // Note, this doesn't necessarily mean the robot JUST got hit, only that that is the most recent thing that happened.
  480. {
  481. { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
  482. { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
  483. { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
  484. { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
  485. { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
  486. { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
  487. { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN}
  488. }
  489. };
  490. ubyte john_cheats_2[2*JOHN_CHEATS_SIZE_2] = { KEY_P ^ 0x00 ^ 0x43, 0x66,
  491. KEY_O ^ 0x10 ^ 0x43, 0x11,
  492. KEY_R ^ 0x20 ^ 0x43, 0x8,
  493. KEY_G ^ 0x30 ^ 0x43, 0x2,
  494. KEY_Y ^ 0x40 ^ 0x43, 0x0,
  495. KEY_S ^ 0x50 ^ 0x43 };
  496. // ---------------------------------------------------------
  497. // On entry, N_robot_types had darn sure better be set.
  498. // Mallocs N_robot_types robot_info structs into global Robot_info.
  499. void init_ai_system(void)
  500. {
  501. #if 0
  502. int i;
  503. mprintf((0, "Trying to malloc %i bytes for Robot_info.\n", N_robot_types * sizeof(*Robot_info)));
  504. Robot_info = (robot_info *) malloc( N_robot_types * sizeof(*Robot_info) );
  505. mprintf((0, "Robot_info = %i\n", Robot_info));
  506. for (i=0; i<N_robot_types; i++) {
  507. Robot_info[i].field_of_view = F1_0/2;
  508. Robot_info[i].firing_wait = F1_0;
  509. Robot_info[i].turn_time = F1_0*2;
  510. Robot_info[i].fire_power = F1_0;
  511. Robot_info[i].shield = F1_0/2;
  512. Robot_info[i].max_speed = F1_0*10;
  513. Robot_info[i].always_0xabcd = 0xabcd;
  514. }
  515. #endif
  516. }
  517. void john_cheat_func_1(int key)
  518. {
  519. if (!Cheats_enabled)
  520. return;
  521. if (key == (john_cheats_1[john_cheats_index_1] ^ (john_cheats_index_1 << 4) ^ 0x34)) {
  522. john_cheats_index_1++;
  523. if (john_cheats_index_1 == JOHN_CHEATS_SIZE_1) {
  524. do_controlcen_destroyed_stuff(NULL);
  525. john_cheats_index_1 = 0;
  526. digi_play_sample( SOUND_CHEATER, F1_0);
  527. }
  528. } else
  529. john_cheats_index_1 = 0;
  530. }
  531. // ---------------------------------------------------------------------------------------------------------------------
  532. // Given a behavior, set initial mode.
  533. int ai_behavior_to_mode(int behavior)
  534. {
  535. switch (behavior) {
  536. case AIB_STILL: return AIM_STILL;
  537. case AIB_NORMAL: return AIM_CHASE_OBJECT;
  538. case AIB_HIDE: return AIM_HIDE;
  539. case AIB_RUN_FROM: return AIM_RUN_FROM_OBJECT;
  540. case AIB_FOLLOW_PATH: return AIM_FOLLOW_PATH;
  541. case AIB_STATION: return AIM_STILL;
  542. default: Int3(); // Contact Mike: Error, illegal behavior type
  543. }
  544. return AIM_STILL;
  545. }
  546. // ---------------------------------------------------------------------------------------------------------------------
  547. // Call every time the player starts a new ship.
  548. void ai_init_boss_for_ship(void)
  549. {
  550. Boss_been_hit = 0;
  551. }
  552. // ---------------------------------------------------------------------------------------------------------------------
  553. // initial_mode == -1 means leave mode unchanged.
  554. void init_ai_object(int objnum, int behavior, int hide_segment)
  555. {
  556. object *objp = &Objects[objnum];
  557. ai_static *aip = &objp->ctype.ai_info;
  558. ai_local *ailp = &Ai_local_info[objnum];
  559. #ifdef DEST_SAT
  560. if (!(Game_mode & GM_MULTI) && Robot_info[objp->id].boss_flag) {
  561. mprintf((0, "Current_level_num = %i, Last_level = %i\n", Current_level_num, Last_level));
  562. if (Current_level_num != Last_level) {
  563. mprintf((0, "Removing boss, object num = %i\n", objnum));
  564. objp->id = 0;
  565. objp->flags |= OF_SHOULD_BE_DEAD;
  566. }
  567. }
  568. #endif
  569. if (behavior == 0) {
  570. // mprintf((0, "Behavior of 0 for object #%i, bashing to AIB_NORMAL.\n", objnum));
  571. behavior = AIB_NORMAL;
  572. objp->ctype.ai_info.behavior = behavior;
  573. }
  574. // mprintf((0, "Initializing object #%i\n", objnum));
  575. // mode is now set from the Robot dialog, so this should get overwritten.
  576. ailp->mode = AIM_STILL;
  577. ailp->previous_visibility = 0;
  578. if (behavior != -1) {
  579. aip->behavior = behavior;
  580. ailp->mode = ai_behavior_to_mode(aip->behavior);
  581. } else if (!((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR))) {
  582. mprintf((0, "[obj %i -> normal] ", objnum));
  583. aip->behavior = AIB_NORMAL;
  584. }
  585. // This is astonishingly stupid! This routine gets called by matcens! KILL KILL KILL!!! Point_segs_free_ptr = Point_segs;
  586. vm_vec_zero(&objp->mtype.phys_info.velocity);
  587. // -- ailp->wait_time = F1_0*5;
  588. ailp->player_awareness_time = 0;
  589. ailp->player_awareness_type = 0;
  590. aip->GOAL_STATE = AIS_SRCH;
  591. aip->CURRENT_STATE = AIS_REST;
  592. ailp->time_player_seen = GameTime;
  593. ailp->next_misc_sound_time = GameTime;
  594. ailp->time_player_sound_attacked = GameTime;
  595. if ((behavior == AIB_HIDE) || (behavior == AIB_FOLLOW_PATH) || (behavior == AIB_STATION) || (behavior == AIB_RUN_FROM)) {
  596. aip->hide_segment = hide_segment;
  597. ailp->goal_segment = hide_segment;
  598. aip->hide_index = -1; // This means the path has not yet been created.
  599. aip->cur_path_index = 0;
  600. }
  601. aip->SKIP_AI_COUNT = 0;
  602. if (Robot_info[objp->id].cloak_type == RI_CLOAKED_ALWAYS)
  603. aip->CLOAKED = 1;
  604. else
  605. aip->CLOAKED = 0;
  606. objp->mtype.phys_info.flags |= (PF_BOUNCE | PF_TURNROLL);
  607. aip->REMOTE_OWNER = -1;
  608. }
  609. void john_cheat_func_2(int key)
  610. {
  611. if (!Cheats_enabled)
  612. return;
  613. if (key == (john_cheats_2[2*john_cheats_index_2] ^ (john_cheats_index_2 << 4) ^ 0x43)) {
  614. john_cheats_index_2++;
  615. if (john_cheats_index_2 == JOHN_CHEATS_SIZE_2) {
  616. Laser_rapid_fire = 0xBADA55;
  617. do_megawow_powerup(200);
  618. john_cheats_index_2 = 0;
  619. digi_play_sample( SOUND_CHEATER, F1_0);
  620. }
  621. } else
  622. john_cheats_index_2 = 0;
  623. }
  624. // ---------------------------------------------------------------------------------------------------------------------
  625. void init_ai_objects(void)
  626. {
  627. int i;
  628. Point_segs_free_ptr = Point_segs;
  629. for (i=0; i<MAX_OBJECTS; i++) {
  630. object *objp = &Objects[i];
  631. if (objp->control_type == CT_AI)
  632. init_ai_object(i, objp->ctype.ai_info.behavior, objp->ctype.ai_info.hide_segment);
  633. }
  634. init_boss_segments(Boss_teleport_segs, &Num_boss_teleport_segs, 1);
  635. #ifndef SHAREWARE
  636. init_boss_segments(Boss_gate_segs, &Num_boss_gate_segs, 0);
  637. #endif
  638. Boss_dying_sound_playing = 0;
  639. Boss_dying = 0;
  640. Boss_been_hit = 0;
  641. #ifndef SHAREWARE
  642. Gate_interval = F1_0*5 - Difficulty_level*F1_0/2;
  643. #endif
  644. Ai_initialized = 1;
  645. }
  646. int Lunacy = 0;
  647. int Diff_save = 1;
  648. fix Firing_wait_copy[MAX_ROBOT_TYPES];
  649. byte Rapidfire_count_copy[MAX_ROBOT_TYPES];
  650. void do_lunacy_on(void)
  651. {
  652. int i;
  653. if ( !Lunacy ) {
  654. Lunacy = 1;
  655. Diff_save = Difficulty_level;
  656. Difficulty_level = NDL-1;
  657. for (i=0; i<MAX_ROBOT_TYPES; i++) {
  658. Firing_wait_copy[i] = Robot_info[i].firing_wait[NDL-1];
  659. Rapidfire_count_copy[i] = Robot_info[i].rapidfire_count[NDL-1];
  660. Robot_info[i].firing_wait[NDL-1] = Robot_info[i].firing_wait[1];
  661. Robot_info[i].rapidfire_count[NDL-1] = Robot_info[i].rapidfire_count[1];
  662. }
  663. }
  664. }
  665. void do_lunacy_off(void)
  666. {
  667. int i;
  668. if ( Lunacy ) {
  669. Lunacy = 0;
  670. for (i=0; i<MAX_ROBOT_TYPES; i++) {
  671. Robot_info[i].firing_wait[NDL-1] = Firing_wait_copy[i];
  672. Robot_info[i].rapidfire_count[NDL-1] = Rapidfire_count_copy[i];
  673. }
  674. Difficulty_level = Diff_save;
  675. }
  676. }
  677. void john_cheat_func_3(int key)
  678. {
  679. if (!Cheats_enabled)
  680. return;
  681. if (key == (john_cheats_3[JOHN_CHEATS_SIZE_3 - john_cheats_index_3] ^ (0x61 + john_cheats_index_3))) {
  682. if (john_cheats_index_3 == 4)
  683. john_cheats_index_3++;
  684. john_cheats_index_3++;
  685. if (john_cheats_index_3 == JOHN_CHEATS_SIZE_3+1) {
  686. if (Lunacy) {
  687. do_lunacy_off();
  688. HUD_init_message( TXT_NO_LUNACY );
  689. } else {
  690. do_lunacy_on();
  691. HUD_init_message( TXT_LUNACY );
  692. digi_play_sample( SOUND_CHEATER, F1_0);
  693. }
  694. john_cheats_index_3 = 0;
  695. }
  696. } else
  697. john_cheats_index_3 = 0;
  698. }
  699. // ----------------------------------------------------------------
  700. // Do *dest = *delta unless:
  701. // *delta is pretty small
  702. // and they are of different signs.
  703. void set_rotvel_and_saturate(fix *dest, fix delta)
  704. {
  705. if ((delta ^ *dest) < 0) {
  706. if (abs(delta) < F1_0/8) {
  707. // mprintf((0, "D"));
  708. *dest = delta/4;
  709. } else
  710. // mprintf((0, "d"));
  711. *dest = delta;
  712. } else {
  713. // mprintf((0, "!"));
  714. *dest = delta;
  715. }
  716. }
  717. //--debug-- #ifndef NDEBUG
  718. //--debug-- int Total_turns=0;
  719. //--debug-- int Prevented_turns=0;
  720. //--debug-- #endif
  721. #define AI_TURN_SCALE 1
  722. #define BABY_SPIDER_ID 14
  723. extern void physics_turn_towards_vector(vms_vector *goal_vector, object *obj, fix rate);
  724. //-------------------------------------------------------------------------------------------
  725. void ai_turn_towards_vector(vms_vector *goal_vector, object *objp, fix rate)
  726. {
  727. vms_vector new_fvec;
  728. fix dot;
  729. if ((objp->id == BABY_SPIDER_ID) && (objp->type == OBJ_ROBOT)) {
  730. physics_turn_towards_vector(goal_vector, objp, rate);
  731. return;
  732. }
  733. new_fvec = *goal_vector;
  734. dot = vm_vec_dot(goal_vector, &objp->orient.fvec);
  735. if (dot < (F1_0 - FrameTime/2)) {
  736. fix mag;
  737. fix new_scale = fixdiv(FrameTime * AI_TURN_SCALE, rate);
  738. vm_vec_scale(&new_fvec, new_scale);
  739. vm_vec_add2(&new_fvec, &objp->orient.fvec);
  740. mag = vm_vec_normalize_quick(&new_fvec);
  741. if (mag < F1_0/256) {
  742. mprintf((1, "Degenerate vector in ai_turn_towards_vector (mag = %7.3f)\n", f2fl(mag)));
  743. new_fvec = *goal_vector; // if degenerate vector, go right to goal
  744. }
  745. }
  746. // // Every 8th time, do a correct matrix create, 7/8 time, do a quick one.
  747. // if (rand() < 0x1000)
  748. vm_vector_2_matrix(&objp->orient, &new_fvec, NULL, &objp->orient.rvec);
  749. // else
  750. // vm_vector_2_matrix_norm(&objp->orient, &new_fvec, NULL, &objp->orient.rvec);
  751. //--{
  752. //--vms_vector tvec;
  753. //--fix mag;
  754. //--tvec = objp->orient.fvec;
  755. //--mag = vm_vec_mag(&tvec);
  756. //--mprintf((0, "mags = %7.3f ", f2fl(mag)));
  757. //--
  758. //--tvec = objp->orient.uvec;
  759. //--mag = vm_vec_mag(&tvec);
  760. //--mprintf((0, "%7.3f ", f2fl(mag)));
  761. //--
  762. //--tvec = objp->orient.rvec;
  763. //--mag = vm_vec_mag(&tvec);
  764. //--mprintf((0, "%7.3f\n", f2fl(mag)));
  765. //--}
  766. //--simpler, but buggy: // The cross product of the forward vector with the right vector is the up vector
  767. //--simpler, but buggy: vm_vec_cross(&new_uvec, &new_fvec, &objp->orient.rvec);
  768. //--simpler, but buggy: vm_vec_cross(&new_rvec, &new_uvec, &new_fvec);
  769. //--simpler, but buggy:
  770. //--simpler, but buggy: objp->orient.fvec = new_fvec;
  771. //--simpler, but buggy: objp->orient.rvec = new_rvec;
  772. //--simpler, but buggy: objp->orient.uvec = new_uvec;
  773. }
  774. // --------------------------------------------------------------------------------------------------------------------
  775. void ai_turn_randomly(vms_vector *vec_to_player, object *obj, fix rate, int previous_visibility)
  776. {
  777. vms_vector curvec;
  778. // Random turning looks too stupid, so 1/4 of time, cheat.
  779. if (previous_visibility)
  780. if (rand() > 0x7400) {
  781. ai_turn_towards_vector(vec_to_player, obj, rate);
  782. return;
  783. }
  784. //--debug-- if (rand() > 0x6000)
  785. //--debug-- Prevented_turns++;
  786. curvec = obj->mtype.phys_info.rotvel;
  787. curvec.y += F1_0/64;
  788. curvec.x += curvec.y/6;
  789. curvec.y += curvec.z/4;
  790. curvec.z += curvec.x/10;
  791. if (abs(curvec.x) > F1_0/8) curvec.x /= 4;
  792. if (abs(curvec.y) > F1_0/8) curvec.y /= 4;
  793. if (abs(curvec.z) > F1_0/8) curvec.z /= 4;
  794. obj->mtype.phys_info.rotvel = curvec;
  795. }
  796. // Overall_agitation affects:
  797. // Widens field of view. Field of view is in range 0..1 (specified in bitmaps.tbl as N/360 degrees).
  798. // Overall_agitation/128 subtracted from field of view, making robots see wider.
  799. // Increases distance to which robot will search to create path to player by Overall_agitation/8 segments.
  800. // Decreases wait between fire times by Overall_agitation/64 seconds.
  801. void john_cheat_func_4(int key)
  802. {
  803. if (!Cheats_enabled)
  804. return;
  805. switch (john_cheats_index_4) {
  806. case 3:
  807. if (key == KEY_T)
  808. john_cheats_index_4++;
  809. else
  810. john_cheats_index_4 = 0;
  811. break;
  812. case 1:
  813. if (key == KEY_L)
  814. john_cheats_index_4++;
  815. else
  816. john_cheats_index_4 = 0;
  817. break;
  818. case 2:
  819. if (key == KEY_E)
  820. john_cheats_index_4++;
  821. else
  822. john_cheats_index_4 = 0;
  823. break;
  824. case 0:
  825. if (key == KEY_P)
  826. john_cheats_index_4++;
  827. break;
  828. case 4:
  829. if (key == KEY_C)
  830. john_cheats_index_4++;
  831. else
  832. john_cheats_index_4 = 0;
  833. break;
  834. case 5:
  835. if (key == KEY_H)
  836. john_cheats_index_4++;
  837. else
  838. john_cheats_index_4 = 0;
  839. break;
  840. case 6:
  841. Ugly_robot_texture = 0;
  842. case 7:
  843. case 8:
  844. if ((key >= KEY_1) && (key <= KEY_0)) {
  845. john_cheats_index_4++;
  846. Ugly_robot_texture *= 10;
  847. if (key != KEY_0)
  848. Ugly_robot_texture += key - 1;
  849. if (john_cheats_index_4 == 9) {
  850. if (Ugly_robot_texture == 999) {
  851. Ugly_robot_cheat = 0;
  852. HUD_init_message( TXT_ROBOT_PAINTING_OFF );
  853. } else {
  854. HUD_init_message( TXT_ROBOT_PAINTING_ON, Ugly_robot_texture );
  855. Ugly_robot_cheat = 0xBADA55;
  856. }
  857. mprintf((0, "Paint value = %i\n", Ugly_robot_texture));
  858. john_cheats_index_4 = 0;
  859. }
  860. } else
  861. john_cheats_index_4 = 0;
  862. break;
  863. default:
  864. john_cheats_index_4 = 0;
  865. }
  866. }
  867. // --------------------------------------------------------------------------------------------------------------------
  868. // Returns:
  869. // 0 Player is not visible from object, obstruction or something.
  870. // 1 Player is visible, but not in field of view.
  871. // 2 Player is visible and in field of view.
  872. // Note: Uses Believed_player_pos as player's position for cloak effect.
  873. // NOTE: Will destructively modify *pos if *pos is outside the mine.
  874. int player_is_visible_from_object(object *objp, vms_vector *pos, fix field_of_view, vms_vector *vec_to_player)
  875. {
  876. fix dot;
  877. fvi_query fq;
  878. fq.p0 = pos;
  879. if ((pos->x != objp->pos.x) || (pos->y != objp->pos.y) || (pos->z != objp->pos.z)) {
  880. int segnum = find_point_seg(pos, objp->segnum);
  881. if (segnum == -1) {
  882. fq.startseg = objp->segnum;
  883. *pos = objp->pos;
  884. mprintf((1, "Object %i, gun is outside mine, moving towards center.\n", objp-Objects));
  885. move_towards_segment_center(objp);
  886. } else
  887. fq.startseg = segnum;
  888. } else
  889. fq.startseg = objp->segnum;
  890. fq.p1 = &Believed_player_pos;
  891. fq.rad = F1_0/4;
  892. fq.thisobjnum = objp-Objects;
  893. fq.ignore_obj_list = NULL;
  894. fq.flags = FQ_TRANSWALL | FQ_CHECK_OBJS; //what about trans walls???
  895. Hit_type = find_vector_intersection(&fq,&Hit_data);
  896. Hit_pos = Hit_data.hit_pnt;
  897. Hit_seg = Hit_data.hit_seg;
  898. if ((Hit_type == HIT_NONE) || ((Hit_type == HIT_OBJECT) && (Hit_data.hit_object == Players[Player_num].objnum))) {
  899. dot = vm_vec_dot(vec_to_player, &objp->orient.fvec);
  900. // mprintf((0, "Fvec = [%5.2f %5.2f %5.2f], vec_to_player = [%5.2f %5.2f %5.2f], dot = %7.3f\n", f2fl(objp->orient.fvec.x), f2fl(objp->orient.fvec.y), f2fl(objp->orient.fvec.z), f2fl(vec_to_player->x), f2fl(vec_to_player->y), f2fl(vec_to_player->z), f2fl(dot)));
  901. if (dot > field_of_view - (Overall_agitation << 9)) {
  902. // mprintf((0, "I can see you!\n"));
  903. return 2;
  904. } else {
  905. // mprintf((0, "Damn, I could see you if I were looking...\n"));
  906. return 1;
  907. }
  908. } else {
  909. // mprintf((0, " ** Where are you? **\n"));
  910. return 0;
  911. }
  912. }
  913. // ------------------------------------------------------------------------------------------------------------------
  914. // Return 1 if animates, else return 0
  915. int do_silly_animation(object *objp)
  916. {
  917. int objnum = objp-Objects;
  918. jointpos *jp_list;
  919. int robot_type, gun_num, robot_state, num_joint_positions;
  920. polyobj_info *pobj_info = &objp->rtype.pobj_info;
  921. ai_static *aip = &objp->ctype.ai_info;
  922. // ai_local *ailp = &Ai_local_info[objnum];
  923. int num_guns, at_goal;
  924. int attack_type;
  925. int flinch_attack_scale = 1;
  926. robot_type = objp->id;
  927. num_guns = Robot_info[robot_type].n_guns;
  928. attack_type = Robot_info[robot_type].attack_type;
  929. if (num_guns == 0) {
  930. // mprintf((0, "Object #%i of type #%i has 0 guns.\n", objp-Objects, robot_type));
  931. return 0;
  932. }
  933. // This is a hack. All positions should be based on goal_state, not GOAL_STATE.
  934. robot_state = Mike_to_matt_xlate[aip->GOAL_STATE];
  935. // previous_robot_state = Mike_to_matt_xlate[aip->CURRENT_STATE];
  936. if (attack_type) // && ((robot_state == AS_FIRE) || (robot_state == AS_RECOIL)))
  937. flinch_attack_scale = Attack_scale;
  938. else if ((robot_state == AS_FLINCH) || (robot_state == AS_RECOIL))
  939. flinch_attack_scale = Flinch_scale;
  940. at_goal = 1;
  941. for (gun_num=0; gun_num <= num_guns; gun_num++) {
  942. int joint;
  943. num_joint_positions = robot_get_anim_state(&jp_list, robot_type, gun_num, robot_state);
  944. for (joint=0; joint<num_joint_positions; joint++) {
  945. fix delta_angle, delta_2;
  946. int jointnum = jp_list[joint].jointnum;
  947. vms_angvec *jp = &jp_list[joint].angles;
  948. vms_angvec *pobjp = &pobj_info->anim_angles[jointnum];
  949. if (jointnum >= Polygon_models[objp->rtype.pobj_info.model_num].n_models) {
  950. Int3(); // Contact Mike: incompatible data, illegal jointnum, problem in pof file?
  951. continue;
  952. }
  953. if (jp->p != pobjp->p) {
  954. if (gun_num == 0)
  955. at_goal = 0;
  956. Ai_local_info[objnum].goal_angles[jointnum].p = jp->p;
  957. delta_angle = jp->p - pobjp->p;
  958. if (delta_angle >= F1_0/2)
  959. delta_2 = -ANIM_RATE;
  960. else if (delta_angle >= 0)
  961. delta_2 = ANIM_RATE;
  962. else if (delta_angle >= -F1_0/2)
  963. delta_2 = -ANIM_RATE;
  964. else
  965. delta_2 = ANIM_RATE;
  966. if (flinch_attack_scale != 1)
  967. delta_2 *= flinch_attack_scale;
  968. Ai_local_info[objnum].delta_angles[jointnum].p = delta_2/DELTA_ANG_SCALE; // complete revolutions per second
  969. }
  970. if (jp->b != pobjp->b) {
  971. if (gun_num == 0)
  972. at_goal = 0;
  973. Ai_local_info[objnum].goal_angles[jointnum].b = jp->b;
  974. delta_angle = jp->b - pobjp->b;
  975. if (delta_angle >= F1_0/2)
  976. delta_2 = -ANIM_RATE;
  977. else if (delta_angle >= 0)
  978. delta_2 = ANIM_RATE;
  979. else if (delta_angle >= -F1_0/2)
  980. delta_2 = -ANIM_RATE;
  981. else
  982. delta_2 = ANIM_RATE;
  983. if (flinch_attack_scale != 1)
  984. delta_2 *= flinch_attack_scale;
  985. Ai_local_info[objnum].delta_angles[jointnum].b = delta_2/DELTA_ANG_SCALE; // complete revolutions per second
  986. }
  987. if (jp->h != pobjp->h) {
  988. if (gun_num == 0)
  989. at_goal = 0;
  990. Ai_local_info[objnum].goal_angles[jointnum].h = jp->h;
  991. delta_angle = jp->h - pobjp->h;
  992. if (delta_angle >= F1_0/2)
  993. delta_2 = -ANIM_RATE;
  994. else if (delta_angle >= 0)
  995. delta_2 = ANIM_RATE;
  996. else if (delta_angle >= -F1_0/2)
  997. delta_2 = -ANIM_RATE;
  998. else
  999. delta_2 = ANIM_RATE;
  1000. if (flinch_attack_scale != 1)
  1001. delta_2 *= flinch_attack_scale;
  1002. Ai_local_info[objnum].delta_angles[jointnum].h = delta_2/DELTA_ANG_SCALE; // complete revolutions per second
  1003. }
  1004. }
  1005. if (at_goal) {
  1006. //ai_static *aip = &objp->ctype.ai_info;
  1007. ai_local *ailp = &Ai_local_info[objp-Objects];
  1008. ailp->achieved_state[gun_num] = ailp->goal_state[gun_num];
  1009. if (ailp->achieved_state[gun_num] == AIS_RECO)
  1010. ailp->goal_state[gun_num] = AIS_FIRE;
  1011. if (ailp->achieved_state[gun_num] == AIS_FLIN)
  1012. ailp->goal_state[gun_num] = AIS_LOCK;
  1013. }
  1014. }
  1015. if (at_goal == 1) //num_guns)
  1016. aip->CURRENT_STATE = aip->GOAL_STATE;
  1017. return 1;
  1018. }
  1019. // ------------------------------------------------------------------------------------------
  1020. // Move all sub-objects in an object towards their goals.
  1021. // Current orientation of object is at: pobj_info.anim_angles
  1022. // Goal orientation of object is at: ai_info.goal_angles
  1023. // Delta orientation of object is at: ai_info.delta_angles
  1024. void ai_frame_animation(object *objp)
  1025. {
  1026. int objnum = objp-Objects;
  1027. int joint;
  1028. int num_joints;
  1029. num_joints = Polygon_models[objp->rtype.pobj_info.model_num].n_models;
  1030. for (joint=1; joint<num_joints; joint++) {
  1031. fix delta_to_goal;
  1032. fix scaled_delta_angle;
  1033. vms_angvec *curangp = &objp->rtype.pobj_info.anim_angles[joint];
  1034. vms_angvec *goalangp = &Ai_local_info[objnum].goal_angles[joint];
  1035. vms_angvec *deltaangp = &Ai_local_info[objnum].delta_angles[joint];
  1036. #ifndef NDEBUG
  1037. if (Ai_animation_test) {
  1038. printf("%i: [%7.3f %7.3f %7.3f] [%7.3f %7.3f %7.3f]\n", joint, f2fl(curangp->p), f2fl(curangp->b), f2fl(curangp->h), f2fl(goalangp->p), f2fl(goalangp->b), f2fl(goalangp->h), f2fl(curangp->p), f2fl(curangp->b), f2fl(curangp->h));
  1039. }
  1040. #endif
  1041. delta_to_goal = goalangp->p - curangp->p;
  1042. if (delta_to_goal > 32767)
  1043. delta_to_goal = delta_to_goal - 65536;
  1044. else if (delta_to_goal < -32767)
  1045. delta_to_goal = 65536 + delta_to_goal;
  1046. if (delta_to_goal) {
  1047. scaled_delta_angle = fixmul(deltaangp->p, FrameTime) * DELTA_ANG_SCALE;
  1048. curangp->p += scaled_delta_angle;
  1049. if (abs(delta_to_goal) < abs(scaled_delta_angle))
  1050. curangp->p = goalangp->p;
  1051. }
  1052. delta_to_goal = goalangp->b - curangp->b;
  1053. if (delta_to_goal > 32767)
  1054. delta_to_goal = delta_to_goal - 65536;
  1055. else if (delta_to_goal < -32767)
  1056. delta_to_goal = 65536 + delta_to_goal;
  1057. if (delta_to_goal) {
  1058. scaled_delta_angle = fixmul(deltaangp->b, FrameTime) * DELTA_ANG_SCALE;
  1059. curangp->b += scaled_delta_angle;
  1060. if (abs(delta_to_goal) < abs(scaled_delta_angle))
  1061. curangp->b = goalangp->b;
  1062. }
  1063. delta_to_goal = goalangp->h - curangp->h;
  1064. if (delta_to_goal > 32767)
  1065. delta_to_goal = delta_to_goal - 65536;
  1066. else if (delta_to_goal < -32767)
  1067. delta_to_goal = 65536 + delta_to_goal;
  1068. if (delta_to_goal) {
  1069. scaled_delta_angle = fixmul(deltaangp->h, FrameTime) * DELTA_ANG_SCALE;
  1070. curangp->h += scaled_delta_angle;
  1071. if (abs(delta_to_goal) < abs(scaled_delta_angle))
  1072. curangp->h = goalangp->h;
  1073. }
  1074. }
  1075. }
  1076. // ----------------------------------------------------------------------------------
  1077. void set_next_fire_time(ai_local *ailp, robot_info *robptr)
  1078. {
  1079. ailp->rapidfire_count++;
  1080. if (ailp->rapidfire_count < robptr->rapidfire_count[Difficulty_level]) {
  1081. ailp->next_fire = min(F1_0/8, robptr->firing_wait[Difficulty_level]/2);
  1082. } else {
  1083. ailp->rapidfire_count = 0;
  1084. ailp->next_fire = robptr->firing_wait[Difficulty_level];
  1085. }
  1086. }
  1087. // ----------------------------------------------------------------------------------
  1088. // When some robots collide with the player, they attack.
  1089. // If player is cloaked, then robot probably didn't actually collide, deal with that here.
  1090. void do_ai_robot_hit_attack(object *robot, object *player, vms_vector *collision_point)
  1091. {
  1092. ai_local *ailp = &Ai_local_info[robot-Objects];
  1093. robot_info *robptr = &Robot_info[robot->id];
  1094. //#ifndef NDEBUG
  1095. if (!Robot_firing_enabled)
  1096. return;
  1097. //#endif
  1098. // If player is dead, stop firing.
  1099. if (Objects[Players[Player_num].objnum].type == OBJ_GHOST)
  1100. return;
  1101. if (robptr->attack_type == 1) {
  1102. if (ailp->next_fire <= 0) {
  1103. if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
  1104. if (vm_vec_dist_quick(&ConsoleObject->pos, &robot->pos) < robot->size + ConsoleObject->size + F1_0*2)
  1105. collide_player_and_nasty_robot( player, robot, collision_point );
  1106. robot->ctype.ai_info.GOAL_STATE = AIS_RECO;
  1107. set_next_fire_time(ailp, robptr);
  1108. }
  1109. }
  1110. }
  1111. extern int Player_exploded;
  1112. // --------------------------------------------------------------------------------------------------------------------
  1113. // Note: Parameter vec_to_player is only passed now because guns which aren't on the forward vector from the
  1114. // center of the robot will not fire right at the player. We need to aim the guns at the player. Barring that, we cheat.
  1115. // When this routine is complete, the parameter vec_to_player should not be necessary.
  1116. void ai_fire_laser_at_player(object *obj, vms_vector *fire_point)
  1117. {
  1118. int objnum = obj-Objects;
  1119. ai_local *ailp = &Ai_local_info[objnum];
  1120. robot_info *robptr = &Robot_info[obj->id];
  1121. vms_vector fire_vec;
  1122. vms_vector bpp_diff;
  1123. if (!Robot_firing_enabled)
  1124. return;
  1125. #ifndef NDEBUG
  1126. // We should never be coming here for the green guy, as he has no laser!
  1127. if (robptr->attack_type == 1)
  1128. Int3(); // Contact Mike: This is impossible.
  1129. #endif
  1130. if (obj->control_type == CT_MORPH)
  1131. return;
  1132. // If player is exploded, stop firing.
  1133. if (Player_exploded)
  1134. return;
  1135. // If player is cloaked, maybe don't fire based on how long cloaked and randomness.
  1136. if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
  1137. fix cloak_time = Ai_cloak_info[objnum % MAX_AI_CLOAK_INFO].last_time;
  1138. if (GameTime - cloak_time > CLOAK_TIME_MAX/4)
  1139. if (rand() > fixdiv(GameTime - cloak_time, CLOAK_TIME_MAX)/2) {
  1140. set_next_fire_time(ailp, robptr);
  1141. return;
  1142. }
  1143. }
  1144. //-- // Find segment containing laser fire position. If the robot is straddling a segment, the position from
  1145. //-- // which it fires may be in a different segment, which is bad news for find_vector_intersection. So, cast
  1146. //-- // a ray from the object center (whose segment we know) to the laser position. Then, in the call to Laser_create_new
  1147. //-- // use the data returned from this call to find_vector_intersection.
  1148. //-- // Note that while find_vector_intersection is pretty slow, it is not terribly slow if the destination point is
  1149. //-- // in the same segment as the source point.
  1150. //--
  1151. //-- fq.p0 = &obj->pos;
  1152. //-- fq.startseg = obj->segnum;
  1153. //-- fq.p1 = fire_point;
  1154. //-- fq.rad = 0;
  1155. //-- fq.thisobjnum = obj-Objects;
  1156. //-- fq.ignore_obj_list = NULL;
  1157. //-- fq.flags = FQ_TRANSWALL | FQ_CHECK_OBJS; //what about trans walls???
  1158. //--
  1159. //-- fate = find_vector_intersection(&fq, &hit_data);
  1160. //-- if (fate != HIT_NONE)
  1161. //-- return;
  1162. // Set position to fire at based on difficulty level.
  1163. bpp_diff.x = Believed_player_pos.x + (rand()-16384) * (NDL-Difficulty_level-1) * 4;
  1164. bpp_diff.y = Believed_player_pos.y + (rand()-16384) * (NDL-Difficulty_level-1) * 4;
  1165. bpp_diff.z = Believed_player_pos.z + (rand()-16384) * (NDL-Difficulty_level-1) * 4;
  1166. // Half the time fire at the player, half the time lead the player.
  1167. if (rand() > 16384) {
  1168. vm_vec_normalized_dir_quick(&fire_vec, &bpp_diff, fire_point);
  1169. } else {
  1170. vms_vector player_direction_vector;
  1171. vm_vec_sub(&player_direction_vector, &bpp_diff, &bpp_diff);
  1172. // If player is not moving, fire right at him!
  1173. // Note: If the robot fires in the direction of its forward vector, this is bad because the weapon does not
  1174. // come out from the center of the robot; it comes out from the side. So it is common for the weapon to miss
  1175. // its target. Ideally, we want to point the guns at the player. For now, just fire right at the player.
  1176. if ((abs(player_direction_vector.x < 0x10000)) && (abs(player_direction_vector.y < 0x10000)) && (abs(player_direction_vector.z < 0x10000))) {
  1177. vm_vec_normalized_dir_quick(&fire_vec, &bpp_diff, fire_point);
  1178. // Player is moving. Determine where the player will be at the end of the next frame if he doesn't change his
  1179. // behavior. Fire at exactly that point. This isn't exactly what you want because it will probably take the laser
  1180. // a different amount of time to get there, since it will probably be a different distance from the player.
  1181. // So, that's why we write games, instead of guiding missiles...
  1182. } else {
  1183. vm_vec_sub(&fire_vec, &bpp_diff, fire_point);
  1184. vm_vec_scale(&fire_vec,fixmul(Weapon_info[Robot_info[obj->id].weapon_type].speed[Difficulty_level], FrameTime));
  1185. vm_vec_add2(&fire_vec, &player_direction_vector);
  1186. vm_vec_normalize_quick(&fire_vec);
  1187. }
  1188. }
  1189. //#ifndef NDEBUG
  1190. // if (robptr->boss_flag)
  1191. // mprintf((0, "Boss (%i) fires!\n", obj-Objects));
  1192. //#endif
  1193. Laser_create_new_easy( &fire_vec, fire_point, obj-Objects, robptr->weapon_type, 1);
  1194. #ifndef SHAREWARE
  1195. #ifdef NETWORK
  1196. if (Game_mode & GM_MULTI)
  1197. {
  1198. ai_multi_send_robot_position(objnum, -1);
  1199. multi_send_robot_fire(objnum, obj->ctype.ai_info.CURRENT_GUN, &fire_vec);
  1200. }
  1201. #endif
  1202. #endif
  1203. create_awareness_event(obj, PA_NEARBY_ROBOT_FIRED);
  1204. set_next_fire_time(ailp, robptr);
  1205. // If the boss fired, allow him to teleport very soon (right after firing, cool!), pending other factors.
  1206. if (robptr->boss_flag)
  1207. Last_teleport_time -= Boss_teleport_interval/2;
  1208. }
  1209. // --------------------------------------------------------------------------------------------------------------------
  1210. // vec_goal must be normalized, or close to it.
  1211. void move_towards_vector(object *objp, vms_vector *vec_goal)
  1212. {
  1213. physics_info *pptr = &objp->mtype.phys_info;
  1214. fix speed, dot, max_speed;
  1215. robot_info *robptr = &Robot_info[objp->id];
  1216. vms_vector vel;
  1217. // Trying to move towards player. If forward vector much different than velocity vector,
  1218. // bash velocity vector twice as much towards player as usual.
  1219. vel = pptr->velocity;
  1220. vm_vec_normalize_quick(&vel);
  1221. dot = vm_vec_dot(&vel, &objp->orient.fvec);
  1222. if (dot < 3*F1_0/4) {
  1223. // This funny code is supposed to slow down the robot and move his velocity towards his direction
  1224. // more quickly than the general code
  1225. //-! mprintf((0, "Th "));
  1226. pptr->velocity.x = pptr->velocity.x/2 + fixmul(vec_goal->x, FrameTime*32);
  1227. pptr->velocity.y = pptr->velocity.y/2 + fixmul(vec_goal->y, FrameTime*32);
  1228. pptr->velocity.z = pptr->velocity.z/2 + fixmul(vec_goal->z, FrameTime*32);
  1229. } else {
  1230. //-! mprintf((0, "Tn "));
  1231. pptr->velocity.x += fixmul(vec_goal->x, FrameTime*64) * (Difficulty_level+5)/4;
  1232. pptr->velocity.y += fixmul(vec_goal->y, FrameTime*64) * (Difficulty_level+5)/4;
  1233. pptr->velocity.z += fixmul(vec_goal->z, FrameTime*64) * (Difficulty_level+5)/4;
  1234. }
  1235. speed = vm_vec_mag_quick(&pptr->velocity);
  1236. max_speed = robptr->max_speed[Difficulty_level];
  1237. // Green guy attacks twice as fast as he moves away.
  1238. if (robptr->attack_type == 1)
  1239. max_speed *= 2;
  1240. if (speed > max_speed) {
  1241. pptr->velocity.x = (pptr->velocity.x*3)/4;
  1242. pptr->velocity.y = (pptr->velocity.y*3)/4;
  1243. pptr->velocity.z = (pptr->velocity.z*3)/4;
  1244. }
  1245. }
  1246. // --------------------------------------------------------------------------------------------------------------------
  1247. void move_towards_player(object *objp, vms_vector *vec_to_player)
  1248. // vec_to_player must be normalized, or close to it.
  1249. {
  1250. move_towards_vector(objp, vec_to_player);
  1251. }
  1252. // --------------------------------------------------------------------------------------------------------------------
  1253. // I am ashamed of this: fast_flag == -1 means normal slide about. fast_flag = 0 means no evasion.
  1254. void move_around_player(object *objp, vms_vector *vec_to_player, int fast_flag)
  1255. {
  1256. physics_info *pptr = &objp->mtype.phys_info;
  1257. fix speed;
  1258. robot_info *robptr = &Robot_info[objp->id];
  1259. int objnum = objp-Objects;
  1260. int dir;
  1261. int dir_change;
  1262. fix ft;
  1263. vms_vector evade_vector;
  1264. int count=0;
  1265. if (fast_flag == 0)
  1266. return;
  1267. dir_change = 48;
  1268. ft = FrameTime;
  1269. if (ft < F1_0/32) {
  1270. dir_change *= 8;
  1271. count += 3;
  1272. } else
  1273. while (ft < F1_0/4) {
  1274. dir_change *= 2;
  1275. ft *= 2;
  1276. count++;
  1277. }
  1278. dir = (FrameCount + (count+1) * (objnum*8 + objnum*4 + objnum)) & dir_change;
  1279. dir >>= (4+count);
  1280. Assert((dir >= 0) && (dir <= 3));
  1281. switch (dir) {
  1282. case 0:
  1283. evade_vector.x = fixmul(vec_to_player->z, FrameTime*32);
  1284. evade_vector.y = fixmul(vec_to_player->y, FrameTime*32);
  1285. evade_vector.z = fixmul(-vec_to_player->x, FrameTime*32);
  1286. break;
  1287. case 1:
  1288. evade_vector.x = fixmul(-vec_to_player->z, FrameTime*32);
  1289. evade_vector.y = fixmul(vec_to_player->y, FrameTime*32);
  1290. evade_vector.z = fixmul(vec_to_player->x, FrameTime*32);
  1291. break;
  1292. case 2:
  1293. evade_vector.x = fixmul(-vec_to_player->y, FrameTime*32);
  1294. evade_vector.y = fixmul(vec_to_player->x, FrameTime*32);
  1295. evade_vector.z = fixmul(vec_to_player->z, FrameTime*32);
  1296. break;
  1297. case 3:
  1298. evade_vector.x = fixmul(vec_to_player->y, FrameTime*32);
  1299. evade_vector.y = fixmul(-vec_to_player->x, FrameTime*32);
  1300. evade_vector.z = fixmul(vec_to_player->z, FrameTime*32);
  1301. break;
  1302. }
  1303. // Note: -1 means normal circling about the player. > 0 means fast evasion.
  1304. if (fast_flag > 0) {
  1305. fix dot;
  1306. // Only take evasive action if looking at player.
  1307. // Evasion speed is scaled by percentage of shields left so wounded robots evade less effectively.
  1308. dot = vm_vec_dot(vec_to_player, &objp->orient.fvec);
  1309. if ((dot > robptr->field_of_view[Difficulty_level]) && !(ConsoleObject->flags & PLAYER_FLAGS_CLOAKED)) {
  1310. fix damage_scale;
  1311. damage_scale = fixdiv(objp->shields, robptr->strength);
  1312. if (damage_scale > F1_0)
  1313. damage_scale = F1_0; // Just in case...
  1314. else if (damage_scale < 0)
  1315. damage_scale = 0; // Just in case...
  1316. vm_vec_scale(&evade_vector, i2f(fast_flag) + damage_scale);
  1317. }
  1318. }
  1319. pptr->velocity.x += evade_vector.x;
  1320. pptr->velocity.y += evade_vector.y;
  1321. pptr->velocity.z += evade_vector.z;
  1322. speed = vm_vec_mag_quick(&pptr->velocity);
  1323. if (speed > robptr->max_speed[Difficulty_level]) {
  1324. pptr->velocity.x = (pptr->velocity.x*3)/4;
  1325. pptr->velocity.y = (pptr->velocity.y*3)/4;
  1326. pptr->velocity.z = (pptr->velocity.z*3)/4;
  1327. }
  1328. }
  1329. // --------------------------------------------------------------------------------------------------------------------
  1330. void move_away_from_player(object *objp, vms_vector *vec_to_player, int attack_type)
  1331. {
  1332. fix speed;
  1333. physics_info *pptr = &objp->mtype.phys_info;
  1334. robot_info *robptr = &Robot_info[objp->id];
  1335. int objref;
  1336. pptr->velocity.x -= fixmul(vec_to_player->x, FrameTime*16);
  1337. pptr->velocity.y -= fixmul(vec_to_player->y, FrameTime*16);
  1338. pptr->velocity.z -= fixmul(vec_to_player->z, FrameTime*16);
  1339. if (attack_type) {
  1340. // Get value in 0..3 to choose evasion direction.
  1341. objref = ((objp-Objects) ^ ((FrameCount + 3*(objp-Objects)) >> 5)) & 3;
  1342. switch (objref) {
  1343. case 0: vm_vec_scale_add2(&pptr->velocity, &objp->orient.uvec, FrameTime << 5); break;
  1344. case 1: vm_vec_scale_add2(&pptr->velocity, &objp->orient.uvec, -FrameTime << 5); break;
  1345. case 2: vm_vec_scale_add2(&pptr->velocity, &objp->orient.rvec, FrameTime << 5); break;
  1346. case 3: vm_vec_scale_add2(&pptr->velocity, &objp->orient.rvec, -FrameTime << 5); break;
  1347. default: Int3(); // Impossible, bogus value on objref, must be in 0..3
  1348. }
  1349. }
  1350. speed = vm_vec_mag_quick(&pptr->velocity);
  1351. if (speed > robptr->max_speed[Difficulty_level]) {
  1352. pptr->velocity.x = (pptr->velocity.x*3)/4;
  1353. pptr->velocity.y = (pptr->velocity.y*3)/4;
  1354. pptr->velocity.z = (pptr->velocity.z*3)/4;
  1355. }
  1356. //--old-- fix speed, dot;
  1357. //--old-- physics_info *pptr = &objp->mtype.phys_info;
  1358. //--old-- robot_info *robptr = &Robot_info[objp->id];
  1359. //--old--
  1360. //--old-- // Trying to move away from player. If forward vector much different than velocity vector,
  1361. //--old-- // bash velocity vector twice as much away from player as usual.
  1362. //--old-- dot = vm_vec_dot(&pptr->velocity, &objp->orient.fvec);
  1363. //--old-- if (dot > -3*F1_0/4) {
  1364. //--old-- // This funny code is supposed to slow down the robot and move his velocity towards his direction
  1365. //--old-- // more quickly than the general code
  1366. //--old-- pptr->velocity.x = pptr->velocity.x/2 - fixmul(vec_to_player->x, FrameTime*16);
  1367. //--old-- pptr->velocity.y = pptr->velocity.y/2 - fixmul(vec_to_player->y, FrameTime*16);
  1368. //--old-- pptr->velocity.z = pptr->velocity.z/2 - fixmul(vec_to_player->z, FrameTime*16);
  1369. //--old-- } else {
  1370. //--old-- pptr->velocity.x -= fixmul(vec_to_player->x, FrameTime*16);
  1371. //--old-- pptr->velocity.y -= fixmul(vec_to_player->y, FrameTime*16);
  1372. //--old-- pptr->velocity.z -= fixmul(vec_to_player->z, FrameTime*16);
  1373. //--old-- }
  1374. //--old--
  1375. //--old-- speed = vm_vec_mag_quick(&pptr->velocity);
  1376. //--old--
  1377. //--old-- if (speed > robptr->max_speed[Difficulty_level]) {
  1378. //--old-- pptr->velocity.x = (pptr->velocity.x*3)/4;
  1379. //--old-- pptr->velocity.y = (pptr->velocity.y*3)/4;
  1380. //--old-- pptr->velocity.z = (pptr->velocity.z*3)/4;
  1381. //--old-- }
  1382. }
  1383. // --------------------------------------------------------------------------------------------------------------------
  1384. // Move towards, away_from or around player.
  1385. // Also deals with evasion.
  1386. // If the flag evade_only is set, then only allowed to evade, not allowed to move otherwise (must have mode == AIM_STILL).
  1387. void ai_move_relative_to_player(object *objp, ai_local *ailp, fix dist_to_player, vms_vector *vec_to_player, fix circle_distance, int evade_only)
  1388. {
  1389. object *dobjp;
  1390. robot_info *robptr = &Robot_info[objp->id];
  1391. // See if should take avoidance.
  1392. // New way, green guys don't evade: if ((robptr->attack_type == 0) && (objp->ctype.ai_info.danger_laser_num != -1)) {
  1393. if (objp->ctype.ai_info.danger_laser_num != -1) {
  1394. dobjp = &Objects[objp->ctype.ai_info.danger_laser_num];
  1395. if ((dobjp->type == OBJ_WEAPON) && (dobjp->signature == objp->ctype.ai_info.danger_laser_signature)) {
  1396. fix dot, dist_to_laser, field_of_view;
  1397. vms_vector vec_to_laser, laser_fvec;
  1398. field_of_view = Robot_info[objp->id].field_of_view[Difficulty_level];
  1399. vm_vec_sub(&vec_to_laser, &dobjp->pos, &objp->pos);
  1400. dist_to_laser = vm_vec_normalize_quick(&vec_to_laser);
  1401. dot = vm_vec_dot(&vec_to_laser, &objp->orient.fvec);
  1402. if (dot > field_of_view) {
  1403. fix laser_robot_dot;
  1404. vms_vector laser_vec_to_robot;
  1405. // The laser is seen by the robot, see if it might hit the robot.
  1406. // Get the laser's direction. If it's a polyobj, it can be gotten cheaply from the orientation matrix.
  1407. if (dobjp->render_type == RT_POLYOBJ)
  1408. laser_fvec = dobjp->orient.fvec;
  1409. else { // Not a polyobj, get velocity and normalize.
  1410. laser_fvec = dobjp->mtype.phys_info.velocity; //dobjp->orient.fvec;
  1411. vm_vec_normalize_quick(&laser_fvec);
  1412. }
  1413. vm_vec_sub(&laser_vec_to_robot, &objp->pos, &dobjp->pos);
  1414. vm_vec_normalize_quick(&laser_vec_to_robot);
  1415. laser_robot_dot = vm_vec_dot(&laser_fvec, &laser_vec_to_robot);
  1416. if ((laser_robot_dot > F1_0*7/8) && (dist_to_laser < F1_0*80)) {
  1417. int evade_speed;
  1418. ai_evaded = 1;
  1419. evade_speed = Robot_info[objp->id].evade_speed[Difficulty_level];
  1420. move_around_player(objp, vec_to_player, evade_speed);
  1421. }
  1422. }
  1423. return;
  1424. }
  1425. }
  1426. // If only allowed to do evade code, then done.
  1427. // Hmm, perhaps brilliant insight. If want claw-type guys to keep coming, don't return here after evasion.
  1428. if ((!robptr->attack_type) && evade_only)
  1429. return;
  1430. // If we fall out of above, then no object to be avoided.
  1431. objp->ctype.ai_info.danger_laser_num = -1;
  1432. // Green guy selects move around/towards/away based on firing time, not distance.
  1433. if (robptr->attack_type == 1) {
  1434. if (((ailp->next_fire > robptr->firing_wait[Difficulty_level]/4) && (dist_to_player < F1_0*30)) || Player_is_dead) {
  1435. // 1/4 of time, move around player, 3/4 of time, move away from player
  1436. if (rand() < 8192) {
  1437. move_around_player(objp, vec_to_player, -1);
  1438. } else {
  1439. move_away_from_player(objp, vec_to_player, 1);
  1440. }
  1441. } else {
  1442. move_towards_player(objp, vec_to_player);
  1443. }
  1444. } else {
  1445. if (dist_to_player < circle_distance)
  1446. move_away_from_player(objp, vec_to_player, 0);
  1447. else if (dist_to_player < circle_distance*2)
  1448. move_around_player(objp, vec_to_player, -1);
  1449. else
  1450. move_towards_player(objp, vec_to_player);
  1451. }
  1452. }
  1453. // --------------------------------------------------------------------------------------------------------------------
  1454. // Compute a somewhat random, normalized vector.
  1455. void make_random_vector(vms_vector *vec)
  1456. {
  1457. vec->x = (rand() - 16384) | 1; // make sure we don't create null vector
  1458. vec->y = rand() - 16384;
  1459. vec->z = rand() - 16384;
  1460. vm_vec_normalize_quick(vec);
  1461. }
  1462. #ifndef NDEBUG
  1463. void mprintf_animation_info(object *objp)
  1464. {
  1465. ai_static *aip = &objp->ctype.ai_info;
  1466. ai_local *ailp = &Ai_local_info[objp-Objects];
  1467. if (!Ai_info_enabled)
  1468. return;
  1469. mprintf((0, "Goal = "));
  1470. switch (aip->GOAL_STATE) {
  1471. case AIS_NONE: mprintf((0, "NONE ")); break;
  1472. case AIS_REST: mprintf((0, "REST ")); break;
  1473. case AIS_SRCH: mprintf((0, "SRCH ")); break;
  1474. case AIS_LOCK: mprintf((0, "LOCK ")); break;
  1475. case AIS_FLIN: mprintf((0, "FLIN ")); break;
  1476. case AIS_FIRE: mprintf((0, "FIRE ")); break;
  1477. case AIS_RECO: mprintf((0, "RECO ")); break;
  1478. case AIS_ERR_: mprintf((0, "ERR_ ")); break;
  1479. }
  1480. mprintf((0, " Cur = "));
  1481. switch (aip->CURRENT_STATE) {
  1482. case AIS_NONE: mprintf((0, "NONE ")); break;
  1483. case AIS_REST: mprintf((0, "REST ")); break;
  1484. case AIS_SRCH: mprintf((0, "SRCH ")); break;
  1485. case AIS_LOCK: mprintf((0, "LOCK ")); break;
  1486. case AIS_FLIN: mprintf((0, "FLIN ")); break;
  1487. case AIS_FIRE: mprintf((0, "FIRE ")); break;
  1488. case AIS_RECO: mprintf((0, "RECO ")); break;
  1489. case AIS_ERR_: mprintf((0, "ERR_ ")); break;
  1490. }
  1491. mprintf((0, " Aware = "));
  1492. switch (ailp->player_awareness_type) {
  1493. case AIE_FIRE: mprintf((0, "FIRE ")); break;
  1494. case AIE_HITT: mprintf((0, "HITT ")); break;
  1495. case AIE_COLL: mprintf((0, "COLL ")); break;
  1496. case AIE_HURT: mprintf((0, "HURT ")); break;
  1497. }
  1498. mprintf((0, "Next fire = %6.3f, Time = %6.3f\n", f2fl(ailp->next_fire), f2fl(ailp->player_awareness_time)));
  1499. }
  1500. #endif
  1501. // -------------------------------------------------------------------------------------------------------------------
  1502. int Break_on_object = -1;
  1503. void do_firing_stuff(object *obj, int player_visibility, vms_vector *vec_to_player)
  1504. {
  1505. //mprintf((0, "!"));
  1506. if (player_visibility >= 1) {
  1507. // Now, if in robot's field of view, lock onto player
  1508. fix dot = vm_vec_dot(&obj->orient.fvec, vec_to_player);
  1509. //mprintf((0, "dot = %8x ", dot));
  1510. if ((dot >= 7*F1_0/8) || (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
  1511. ai_static *aip = &obj->ctype.ai_info;
  1512. ai_local *ailp = &Ai_local_info[obj-Objects];
  1513. switch (aip->GOAL_STATE) {
  1514. case AIS_NONE:
  1515. case AIS_REST:
  1516. case AIS_SRCH:
  1517. case AIS_LOCK:
  1518. aip->GOAL_STATE = AIS_FIRE;
  1519. if (ailp->player_awareness_type <= PA_NEARBY_ROBOT_FIRED) {
  1520. ailp->player_awareness_type = PA_NEARBY_ROBOT_FIRED;
  1521. ailp->player_awareness_time = PLAYER_AWARENESS_INITIAL_TIME;
  1522. }
  1523. break;
  1524. }
  1525. } else if (dot >= F1_0/2) {
  1526. ai_static *aip = &obj->ctype.ai_info;
  1527. switch (aip->GOAL_STATE) {
  1528. case AIS_NONE:
  1529. case AIS_REST:
  1530. case AIS_SRCH:
  1531. aip->GOAL_STATE = AIS_LOCK;
  1532. break;
  1533. }
  1534. }
  1535. }
  1536. }
  1537. // --------------------------------------------------------------------------------------------------------------------
  1538. // If a hiding robot gets bumped or hit, he decides to find another hiding place.
  1539. void do_ai_robot_hit(object *objp, int type)
  1540. {
  1541. if (objp->control_type == CT_AI) {
  1542. if ((type == PA_WEAPON_ROBOT_COLLISION) || (type == PA_PLAYER_COLLISION))
  1543. switch (objp->ctype.ai_info.behavior) {
  1544. case AIM_HIDE:
  1545. objp->ctype.ai_info.SUBMODE = AISM_GOHIDE;
  1546. break;
  1547. case AIM_STILL:
  1548. Ai_local_info[objp-Objects].mode = AIM_CHASE_OBJECT;
  1549. break;
  1550. }
  1551. }
  1552. }
  1553. #ifndef NDEBUG
  1554. int Do_ai_flag=1;
  1555. int Cvv_test=0;
  1556. int Cvv_last_time[MAX_OBJECTS];
  1557. int Gun_point_hack=0;
  1558. #endif
  1559. #define CHASE_TIME_LENGTH (F1_0*8)
  1560. #define DEFAULT_ROBOT_SOUND_VOLUME F1_0
  1561. int Robot_sound_volume=DEFAULT_ROBOT_SOUND_VOLUME;
  1562. // --------------------------------------------------------------------------------------------------------------------
  1563. // Note: This function could be optimized. Surely player_is_visible_from_object would benefit from the
  1564. // information of a normalized vec_to_player.
  1565. // Return player visibility:
  1566. // 0 not visible
  1567. // 1 visible, but robot not looking at player (ie, on an unobstructed vector)
  1568. // 2 visible and in robot's field of view
  1569. // -1 player is cloaked
  1570. // If the player is cloaked, set vec_to_player based on time player cloaked and last uncloaked position.
  1571. // Updates ailp->previous_visibility if player is not cloaked, in which case the previous visibility is left unchanged
  1572. // and is copied to player_visibility
  1573. void compute_vis_and_vec(object *objp, vms_vector *pos, ai_local *ailp, vms_vector *vec_to_player, int *player_visibility, robot_info *robptr, int *flag)
  1574. {
  1575. if (!*flag) {
  1576. if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
  1577. fix delta_time, dist;
  1578. int cloak_index = (objp-Objects) % MAX_AI_CLOAK_INFO;
  1579. delta_time = GameTime - Ai_cloak_info[cloak_index].last_time;
  1580. if (delta_time > F1_0*2) {
  1581. vms_vector randvec;
  1582. Ai_cloak_info[cloak_index].last_time = GameTime;
  1583. make_random_vector(&randvec);
  1584. vm_vec_scale_add2(&Ai_cloak_info[cloak_index].last_position, &randvec, 8*delta_time );
  1585. }
  1586. dist = vm_vec_normalized_dir_quick(vec_to_player, &Ai_cloak_info[cloak_index].last_position, pos);
  1587. *player_visibility = player_is_visible_from_object(objp, pos, robptr->field_of_view[Difficulty_level], vec_to_player);
  1588. // *player_visibility = 2;
  1589. if ((ailp->next_misc_sound_time < GameTime) && (ailp->next_fire < F1_0) && (dist < F1_0*20)) {
  1590. mprintf((0, "ANGRY! "));
  1591. ailp->next_misc_sound_time = GameTime + (rand() + F1_0) * (7 - Difficulty_level) / 1;
  1592. digi_link_sound_to_pos( robptr->see_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
  1593. }
  1594. } else {
  1595. // Compute expensive stuff -- vec_to_player and player_visibility
  1596. vm_vec_normalized_dir_quick(vec_to_player, &Believed_player_pos, pos);
  1597. if ((vec_to_player->x == 0) && (vec_to_player->y == 0) && (vec_to_player->z == 0)) {
  1598. mprintf((0, "Warning: Player and robot at exactly the same location.\n"));
  1599. vec_to_player->x = F1_0;
  1600. }
  1601. *player_visibility = player_is_visible_from_object(objp, pos, robptr->field_of_view[Difficulty_level], vec_to_player);
  1602. // This horrible code added by MK in desperation on 12/13/94 to make robots wake up as soon as they
  1603. // see you without killing frame rate.
  1604. {
  1605. ai_static *aip = &objp->ctype.ai_info;
  1606. if ((*player_visibility == 2) && (ailp->previous_visibility != 2))
  1607. if ((aip->GOAL_STATE == AIS_REST) || (aip->CURRENT_STATE == AIS_REST)) {
  1608. aip->GOAL_STATE = AIS_FIRE;
  1609. aip->CURRENT_STATE = AIS_FIRE;
  1610. }
  1611. }
  1612. if (!Player_exploded && (ailp->previous_visibility != *player_visibility) && (*player_visibility == 2)) {
  1613. if (ailp->previous_visibility == 0) {
  1614. if (ailp->time_player_seen + F1_0/2 < GameTime) {
  1615. // mprintf((0, "SEE! "));
  1616. digi_link_sound_to_pos( robptr->see_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
  1617. ailp->time_player_sound_attacked = GameTime;
  1618. ailp->next_misc_sound_time = GameTime + F1_0 + rand()*4;
  1619. }
  1620. } else if (ailp->time_player_sound_attacked + F1_0/4 < GameTime) {
  1621. // mprintf((0, "ANGRY! "));
  1622. digi_link_sound_to_pos( robptr->attack_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
  1623. ailp->time_player_sound_attacked = GameTime;
  1624. }
  1625. }
  1626. if ((*player_visibility == 2) && (ailp->next_misc_sound_time < GameTime)) {
  1627. // mprintf((0, "ATTACK! "));
  1628. ailp->next_misc_sound_time = GameTime + (rand() + F1_0) * (7 - Difficulty_level) / 2;
  1629. digi_link_sound_to_pos( robptr->attack_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
  1630. }
  1631. ailp->previous_visibility = *player_visibility;
  1632. }
  1633. *flag = 1;
  1634. if (*player_visibility) {
  1635. ailp->time_player_seen = GameTime;
  1636. }
  1637. }
  1638. }
  1639. // --------------------------------------------------------------------------------------------------------------------
  1640. // Move the object objp to a spot in which it doesn't intersect a wall.
  1641. // It might mean moving it outside its current segment.
  1642. void move_object_to_legal_spot(object *objp)
  1643. {
  1644. vms_vector original_pos = objp->pos;
  1645. int i;
  1646. segment *segp = &Segments[objp->segnum];
  1647. for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
  1648. if (WALL_IS_DOORWAY(segp, i) & WID_FLY_FLAG) {
  1649. vms_vector segment_center, goal_dir;
  1650. fix dist_to_center;
  1651. compute_segment_center(&segment_center, &Segments[segp->children[i]]);
  1652. vm_vec_sub(&goal_dir, &segment_center, &objp->pos);
  1653. dist_to_center = vm_vec_normalize_quick(&goal_dir);
  1654. vm_vec_scale(&goal_dir, objp->size);
  1655. vm_vec_add2(&objp->pos, &goal_dir);
  1656. if (!object_intersects_wall(objp)) {
  1657. int new_segnum = find_point_seg(&objp->pos, objp->segnum);
  1658. if (new_segnum != -1) {
  1659. obj_relink(objp-Objects, new_segnum);
  1660. return;
  1661. }
  1662. } else
  1663. objp->pos = original_pos;
  1664. }
  1665. }
  1666. // Int3(); // Darn you John, you done it again! (But contact Mike)
  1667. mprintf((0, "Note: Killing robot #%i because he's badly stuck outside the mine.\n", objp-Objects));
  1668. apply_damage_to_robot(objp, objp->shields*2, objp-Objects);
  1669. }
  1670. // --------------------------------------------------------------------------------------------------------------------
  1671. // Move object one object radii from current position towards segment center.
  1672. // If segment center is nearer than 2 radii, move it to center.
  1673. void move_towards_segment_center(object *objp)
  1674. {
  1675. int segnum = objp->segnum;
  1676. fix dist_to_center;
  1677. vms_vector segment_center, goal_dir;
  1678. compute_segment_center(&segment_center, &Segments[segnum]);
  1679. vm_vec_sub(&goal_dir, &segment_center, &objp->pos);
  1680. dist_to_center = vm_vec_normalize_quick(&goal_dir);
  1681. if (dist_to_center < objp->size) {
  1682. // Center is nearer than the distance we want to move, so move to center.
  1683. objp->pos = segment_center;
  1684. mprintf((0, "Object #%i moved to center of segment #%i (%7.3f %7.3f %7.3f)\n", objp-Objects, objp->segnum, f2fl(objp->pos.x), f2fl(objp->pos.y), f2fl(objp->pos.z)));
  1685. if (object_intersects_wall(objp)) {
  1686. mprintf((0, "Object #%i still illegal, trying trickier move.\n"));
  1687. move_object_to_legal_spot(objp);
  1688. }
  1689. } else {
  1690. int new_segnum;
  1691. // Move one radii towards center.
  1692. vm_vec_scale(&goal_dir, objp->size);
  1693. vm_vec_add2(&objp->pos, &goal_dir);
  1694. new_segnum = find_point_seg(&objp->pos, objp->segnum);
  1695. if (new_segnum == -1) {
  1696. objp->pos = segment_center;
  1697. move_object_to_legal_spot(objp);
  1698. }
  1699. mprintf((0, "Obj %i moved twrds seg %i (%6.2f %6.2f %6.2f), dists: [%6.2f %6.2f]\n", objp-Objects, objp->segnum, f2fl(objp->pos.x), f2fl(objp->pos.y), f2fl(objp->pos.z), f2fl(vm_vec_dist_quick(&objp->pos, &segment_center)), f2fl(vm_vec_dist_quick(&objp->pos, &segment_center))));
  1700. }
  1701. }
  1702. // -----------------------------------------------------------------------------------------------------------
  1703. // Return true if door can be flown through by a suitable type robot.
  1704. // Only brains and avoid robots can open doors.
  1705. int ai_door_is_openable(object *objp, segment *segp, int sidenum)
  1706. {
  1707. int wall_num;
  1708. // The mighty console object can open all doors (for purposes of determining paths).
  1709. if (objp == ConsoleObject) {
  1710. int wall_num = segp->sides[sidenum].wall_num;
  1711. if (Walls[wall_num].type == WALL_DOOR)
  1712. return 1;
  1713. }
  1714. if ((objp->id == ROBOT_BRAIN) || (objp->ctype.ai_info.behavior == AIB_RUN_FROM)) {
  1715. wall_num = segp->sides[sidenum].wall_num;
  1716. if (wall_num != -1)
  1717. if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].keys == KEY_NONE) && !(Walls[wall_num].flags & WALL_DOOR_LOCKED))
  1718. return 1;
  1719. }
  1720. return 0;
  1721. }
  1722. //--// -----------------------------------------------------------------------------------------------------------
  1723. //--// Return true if object *objp is allowed to open door at wall_num
  1724. //--int door_openable_by_robot(object *objp, int wall_num)
  1725. //--{
  1726. //-- if (objp->id == ROBOT_BRAIN)
  1727. //-- if (Walls[wall_num].keys == KEY_NONE)
  1728. //-- return 1;
  1729. //--
  1730. //-- return 0;
  1731. //--}
  1732. // -----------------------------------------------------------------------------------------------------------
  1733. // Return side of openable door in segment, if any. If none, return -1.
  1734. int openable_doors_in_segment(object *objp)
  1735. {
  1736. int i;
  1737. int segnum = objp->segnum;
  1738. for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
  1739. if (Segments[segnum].sides[i].wall_num != -1) {
  1740. int wall_num = Segments[segnum].sides[i].wall_num;
  1741. if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].keys == KEY_NONE) && (Walls[wall_num].state == WALL_DOOR_CLOSED) && !(Walls[wall_num].flags & WALL_DOOR_LOCKED))
  1742. return i;
  1743. }
  1744. }
  1745. return -1;
  1746. }
  1747. //--unused-- // -----------------------------------------------------------------------------------------------------------
  1748. //--unused-- // For all doors this guy can open in his segment, open them.
  1749. //--unused-- void ai_open_doors_in_segment(object *objp)
  1750. //--unused-- {
  1751. //--unused-- int i;
  1752. //--unused-- int segnum = objp->segnum;
  1753. //--unused--
  1754. //--unused-- for (i=0; i<MAX_SIDES_PER_SEGMENT; i++)
  1755. //--unused-- if (Segments[segnum].sides[i].wall_num != -1) {
  1756. //--unused-- int wall_num = Segments[segnum].sides[i].wall_num;
  1757. //--unused-- if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].keys == KEY_NONE) && (Walls[wall_num].state == WALL_DOOR_CLOSED))
  1758. //--unused-- if (door_openable_by_robot(objp, wall_num)) {
  1759. //--unused-- mprintf((0, "Trying to open door at segment %i, side %i\n", segnum, i));
  1760. //--unused-- wall_open_door(&Segments[segnum], i);
  1761. //--unused-- }
  1762. //--unused-- }
  1763. //--unused-- }
  1764. // --------------------------------------------------------------------------------------------------------------------
  1765. // Return true if a special object (player or control center) is in this segment.
  1766. int special_object_in_seg(int segnum)
  1767. {
  1768. int objnum;
  1769. objnum = Segments[segnum].objects;
  1770. while (objnum != -1) {
  1771. if ((Objects[objnum].type == OBJ_PLAYER) || (Objects[objnum].type == OBJ_CNTRLCEN)) {
  1772. mprintf((0, "Special object of type %i in segment %i\n", Objects[objnum].type, segnum));
  1773. return 1;
  1774. } else
  1775. objnum = Objects[objnum].next;
  1776. }
  1777. return 0;
  1778. }
  1779. // --------------------------------------------------------------------------------------------------------------------
  1780. // Randomly select a segment attached to *segp, reachable by flying.
  1781. int get_random_child(int segnum)
  1782. {
  1783. int sidenum;
  1784. segment *segp = &Segments[segnum];
  1785. sidenum = (rand() * 6) >> 15;
  1786. while (!(WALL_IS_DOORWAY(segp, sidenum) & WID_FLY_FLAG))
  1787. sidenum = (rand() * 6) >> 15;
  1788. segnum = segp->children[sidenum];
  1789. return segnum;
  1790. }
  1791. // --------------------------------------------------------------------------------------------------------------------
  1792. // Return true if placing an object of size size at pos *pos intersects a (player or robot or control center) in segment *segp.
  1793. int check_object_object_intersection(vms_vector *pos, fix size, segment *segp)
  1794. {
  1795. int curobjnum;
  1796. // If this would intersect with another object (only check those in this segment), then try to move.
  1797. curobjnum = segp->objects;
  1798. while (curobjnum != -1) {
  1799. object *curobjp = &Objects[curobjnum];
  1800. if ((curobjp->type == OBJ_PLAYER) || (curobjp->type == OBJ_ROBOT) || (curobjp->type == OBJ_CNTRLCEN)) {
  1801. if (vm_vec_dist_quick(pos, &curobjp->pos) < size + curobjp->size)
  1802. return 1;
  1803. }
  1804. curobjnum = curobjp->next;
  1805. }
  1806. return 0;
  1807. }
  1808. #ifndef SHAREWARE
  1809. // --------------------------------------------------------------------------------------------------------------------
  1810. // Return true if object created, else return false.
  1811. int create_gated_robot( int segnum, int object_id)
  1812. {
  1813. int objnum;
  1814. object *objp;
  1815. segment *segp = &Segments[segnum];
  1816. vms_vector object_pos;
  1817. robot_info *robptr = &Robot_info[object_id];
  1818. int i, count=0;
  1819. fix objsize = Polygon_models[robptr->model_num].rad;
  1820. int default_behavior;
  1821. for (i=0; i<=Highest_object_index; i++)
  1822. if (Objects[i].type == OBJ_ROBOT)
  1823. if (Objects[i].matcen_creator == BOSS_GATE_MATCEN_NUM)
  1824. count++;
  1825. if (count > 2*Difficulty_level + 3) {
  1826. // mprintf((0, "Cannot gate in a robot until you kill one.\n"));
  1827. Last_gate_time = GameTime - 3*Gate_interval/4;
  1828. return 0;
  1829. }
  1830. compute_segment_center(&object_pos, segp);
  1831. pick_random_point_in_seg(&object_pos, segp-Segments);
  1832. // See if legal to place object here. If not, move about in segment and try again.
  1833. if (check_object_object_intersection(&object_pos, objsize, segp)) {
  1834. // mprintf((0, "Can't get in because object collides with something.\n"));
  1835. Last_gate_time = GameTime - 3*Gate_interval/4;
  1836. return 0;
  1837. }
  1838. objnum = obj_create(OBJ_ROBOT, object_id, segnum, &object_pos, &vmd_identity_matrix, objsize, CT_AI, MT_PHYSICS, RT_POLYOBJ);
  1839. if ( objnum < 0 ) {
  1840. // mprintf((1, "Can't get object to gate in robot. Not gating in.\n"));
  1841. Last_gate_time = GameTime - 3*Gate_interval/4;
  1842. return 0;
  1843. }
  1844. mprintf((0, "Gating in object %i in segment %i\n", objnum, segp-Segments));
  1845. #ifdef NETWORK
  1846. Net_create_objnums[0] = objnum; // A convenient global to get objnum back to caller for multiplayer
  1847. #endif
  1848. objp = &Objects[objnum];
  1849. //Set polygon-object-specific data
  1850. objp->rtype.pobj_info.model_num = robptr->model_num;
  1851. objp->rtype.pobj_info.subobj_flags = 0;
  1852. //set Physics info
  1853. objp->mtype.phys_info.mass = robptr->mass;
  1854. objp->mtype.phys_info.drag = robptr->drag;
  1855. objp->mtype.phys_info.flags |= (PF_LEVELLING);
  1856. objp->shields = robptr->strength;
  1857. objp->matcen_creator = BOSS_GATE_MATCEN_NUM; // flag this robot as having been created by the boss.
  1858. default_behavior = AIB_NORMAL;
  1859. if (object_id == 10) // This is a toaster guy!
  1860. default_behavior = AIB_RUN_FROM;
  1861. init_ai_object(objp-Objects, default_behavior, -1 ); // Note, -1 = segment this robot goes to to hide, should probably be something useful
  1862. object_create_explosion(segnum, &object_pos, i2f(10), VCLIP_MORPHING_ROBOT );
  1863. digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, segnum, 0, &object_pos, 0 , F1_0);
  1864. morph_start(objp);
  1865. Last_gate_time = GameTime;
  1866. Players[Player_num].num_robots_level++;
  1867. Players[Player_num].num_robots_total++;
  1868. return 1;
  1869. }
  1870. // --------------------------------------------------------------------------------------------------------------------
  1871. // Make object objp gate in a robot.
  1872. // The process of him bringing in a robot takes one second.
  1873. // Then a robot appears somewhere near the player.
  1874. // Return true if robot successfully created, else return false
  1875. int gate_in_robot(int type, int segnum)
  1876. {
  1877. if (segnum < 0)
  1878. segnum = Boss_gate_segs[(rand() * Num_boss_gate_segs) >> 15];
  1879. Assert((segnum >= 0) && (segnum <= Highest_segment_index));
  1880. return create_gated_robot(segnum, type);
  1881. }
  1882. #endif
  1883. //// --------------------------------------------------------------------------------------------------------------------
  1884. //// Return true if some distance function of vertices implies segment is too small for object.
  1885. //int segment_too_small_for_object(object *objp, segment *segp)
  1886. //{
  1887. // int i;
  1888. // fix threshold_distance;
  1889. //
  1890. // threshold_distance = objp->size*2;
  1891. //
  1892. // for (i=1; i<MAX_VERTICES_PER_SEGMENT; i++)
  1893. // if (vm_vec_dist_quick(&Vertices[segp->verts[i]], &Vertices[segp->verts[i-1]]) < threshold_distance) {
  1894. //#ifndef NDEBUG
  1895. // fix dist = vm_vec_dist_quick(&Vertices[segp->verts[i]], &Vertices[segp->verts[i-1]]);
  1896. //#endif
  1897. // mprintf((0, "Seg %i too small for obj %i (sz=%7.3f), verts %i, %i only %7.3f apart\n", segp-Segments, objp-Objects, f2fl(objp->size), segp->verts[i], segp->verts[i-1], f2fl(dist)));
  1898. // return 1;
  1899. // }
  1900. //
  1901. // return 0;
  1902. //}
  1903. //--unused-- int Shown_all_segments=0;
  1904. // --------------------------------------------------------------------------------------------------------------------
  1905. int boss_fits_in_seg(object *boss_objp, int segnum)
  1906. {
  1907. vms_vector segcenter;
  1908. int boss_objnum = boss_objp-Objects;
  1909. int posnum;
  1910. compute_segment_center(&segcenter, &Segments[segnum]);
  1911. for (posnum=0; posnum<9; posnum++) {
  1912. if (posnum > 0) {
  1913. vms_vector vertex_pos;
  1914. Assert((posnum-1 >= 0) && (posnum-1 < 8));
  1915. vertex_pos = Vertices[Segments[segnum].verts[posnum-1]];
  1916. vm_vec_avg(&boss_objp->pos, &vertex_pos, &segcenter);
  1917. } else
  1918. boss_objp->pos = segcenter;
  1919. obj_relink(boss_objnum, segnum);
  1920. if (!object_intersects_wall(boss_objp))
  1921. return 1;
  1922. }
  1923. return 0;
  1924. }
  1925. #define QUEUE_SIZE 256
  1926. // --------------------------------------------------------------------------------------------------------------------
  1927. // Create list of segments boss is allowed to teleport to at segptr.
  1928. // Set *num_segs.
  1929. // Boss is allowed to teleport to segments he fits in (calls object_intersects_wall) and
  1930. // he can reach from his initial position (calls find_connected_distance).
  1931. // If size_check is set, then only add segment if boss can fit in it, else any segment is legal.
  1932. void init_boss_segments(short segptr[], int *num_segs, int size_check)
  1933. {
  1934. int boss_objnum=-1;
  1935. int i;
  1936. *num_segs = 0;
  1937. #ifdef EDITOR
  1938. N_selected_segs = 0;
  1939. #endif
  1940. // See if there is a boss. If not, quick out.
  1941. for (i=0; i<=Highest_object_index; i++)
  1942. if ((Objects[i].type == OBJ_ROBOT) && (Robot_info[Objects[i].id].boss_flag)) {
  1943. Assert(boss_objnum == -1); // There are two bosses in this mine! i and boss_objnum!
  1944. boss_objnum = i;
  1945. }
  1946. if (boss_objnum != -1) {
  1947. int original_boss_seg;
  1948. vms_vector original_boss_pos;
  1949. object *boss_objp = &Objects[boss_objnum];
  1950. int head, tail;
  1951. int seg_queue[QUEUE_SIZE];
  1952. //ALREADY IN RENDER.H byte visited[MAX_SEGMENTS];
  1953. fix boss_size_save;
  1954. boss_size_save = boss_objp->size;
  1955. boss_objp->size = fixmul((F1_0/4)*3, boss_objp->size);
  1956. original_boss_seg = boss_objp->segnum;
  1957. original_boss_pos = boss_objp->pos;
  1958. head = 0;
  1959. tail = 0;
  1960. seg_queue[head++] = original_boss_seg;
  1961. segptr[(*num_segs)++] = original_boss_seg;
  1962. #ifdef EDITOR
  1963. Selected_segs[N_selected_segs++] = original_boss_seg;
  1964. #endif
  1965. for (i=0; i<=Highest_segment_index; i++)
  1966. visited[i] = 0;
  1967. while (tail != head) {
  1968. int sidenum;
  1969. segment *segp = &Segments[seg_queue[tail++]];
  1970. tail &= QUEUE_SIZE-1;
  1971. for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
  1972. if (WALL_IS_DOORWAY(segp, sidenum) & WID_FLY_FLAG) {
  1973. if (visited[segp->children[sidenum]] == 0) {
  1974. seg_queue[head++] = segp->children[sidenum];
  1975. visited[segp->children[sidenum]] = 1;
  1976. head &= QUEUE_SIZE-1;
  1977. if (head > tail) {
  1978. if (head == tail + QUEUE_SIZE-1)
  1979. Int3(); // queue overflow. Make it bigger!
  1980. } else
  1981. if (head+QUEUE_SIZE == tail + QUEUE_SIZE-1)
  1982. Int3(); // queue overflow. Make it bigger!
  1983. if ((!size_check) || boss_fits_in_seg(boss_objp, segp->children[sidenum])) {
  1984. segptr[(*num_segs)++] = segp->children[sidenum];
  1985. #ifdef EDITOR
  1986. Selected_segs[N_selected_segs++] = segp->children[sidenum];
  1987. #endif
  1988. if (*num_segs >= MAX_BOSS_TELEPORT_SEGS) {
  1989. mprintf((1, "Warning: Too many boss teleport segments. Found %i after searching %i/%i segments.\n", MAX_BOSS_TELEPORT_SEGS, segp->children[sidenum], Highest_segment_index+1));
  1990. tail = head;
  1991. }
  1992. }
  1993. }
  1994. }
  1995. }
  1996. }
  1997. boss_objp->size = boss_size_save;
  1998. boss_objp->pos = original_boss_pos;
  1999. obj_relink(boss_objnum, original_boss_seg);
  2000. }
  2001. }
  2002. // --------------------------------------------------------------------------------------------------------------------
  2003. void teleport_boss(object *objp)
  2004. {
  2005. int rand_segnum;
  2006. vms_vector boss_dir;
  2007. int rand_seg;
  2008. Assert(Num_boss_teleport_segs > 0);
  2009. // Pick a random segment from the list of boss-teleportable-to segments.
  2010. rand_seg = (rand() * Num_boss_teleport_segs) >> 15;
  2011. rand_segnum = Boss_teleport_segs[rand_seg];
  2012. Assert((rand_segnum >= 0) && (rand_segnum <= Highest_segment_index));
  2013. #ifndef SHAREWARE
  2014. #ifdef NETWORK
  2015. if (Game_mode & GM_MULTI)
  2016. multi_send_boss_actions(objp-Objects, 1, rand_seg, 0);
  2017. #endif
  2018. #endif
  2019. compute_segment_center(&objp->pos, &Segments[rand_segnum]);
  2020. obj_relink(objp-Objects, rand_segnum);
  2021. Last_teleport_time = GameTime;
  2022. // make boss point right at player
  2023. vm_vec_sub(&boss_dir, &Objects[Players[Player_num].objnum].pos, &objp->pos);
  2024. vm_vector_2_matrix(&objp->orient, &boss_dir, NULL, NULL);
  2025. digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, rand_segnum, 0, &objp->pos, 0 , F1_0);
  2026. digi_kill_sound_linked_to_object( objp-Objects);
  2027. digi_link_sound_to_object2( SOUND_BOSS_SHARE_SEE, objp-Objects, 1, F1_0, F1_0*512 ); // F1_0*512 means play twice as loud
  2028. #ifndef NDEBUG
  2029. mprintf((0, "Boss teleported to segment %i\n", rand_segnum));
  2030. #endif
  2031. // After a teleport, boss can fire right away.
  2032. Ai_local_info[objp-Objects].next_fire = 0;
  2033. }
  2034. // ----------------------------------------------------------------------
  2035. void start_boss_death_sequence(object *objp)
  2036. {
  2037. if (Robot_info[objp->id].boss_flag) {
  2038. Boss_dying = 1;
  2039. Boss_dying_start_time = GameTime;
  2040. }
  2041. }
  2042. // ----------------------------------------------------------------------
  2043. void do_boss_dying_frame(object *objp)
  2044. {
  2045. fix boss_roll_val, temp;
  2046. boss_roll_val = fixdiv(GameTime - Boss_dying_start_time, BOSS_DEATH_DURATION);
  2047. fix_sincos(fixmul(boss_roll_val, boss_roll_val), &temp, &objp->mtype.phys_info.rotvel.x);
  2048. fix_sincos(boss_roll_val, &temp, &objp->mtype.phys_info.rotvel.y);
  2049. fix_sincos(boss_roll_val-F1_0/8, &temp, &objp->mtype.phys_info.rotvel.z);
  2050. objp->mtype.phys_info.rotvel.x = (GameTime - Boss_dying_start_time)/9;
  2051. objp->mtype.phys_info.rotvel.y = (GameTime - Boss_dying_start_time)/5;
  2052. objp->mtype.phys_info.rotvel.z = (GameTime - Boss_dying_start_time)/7;
  2053. if (Boss_dying_start_time + BOSS_DEATH_DURATION - BOSS_DEATH_SOUND_DURATION < GameTime) {
  2054. if (!Boss_dying_sound_playing) {
  2055. mprintf((0, "Starting boss death sound!\n"));
  2056. Boss_dying_sound_playing = 1;
  2057. digi_link_sound_to_object2( SOUND_BOSS_SHARE_DIE, objp-Objects, 0, F1_0*4, F1_0*1024 ); // F1_0*512 means play twice as loud
  2058. } else if (rand() < FrameTime*16)
  2059. create_small_fireball_on_object(objp, (F1_0 + rand()) * 8, 0);
  2060. } else if (rand() < FrameTime*8)
  2061. create_small_fireball_on_object(objp, (F1_0/2 + rand()) * 8, 1);
  2062. if (Boss_dying_start_time + BOSS_DEATH_DURATION < GameTime) {
  2063. do_controlcen_destroyed_stuff(NULL);
  2064. explode_object(objp, F1_0/4);
  2065. digi_link_sound_to_object2(SOUND_BADASS_EXPLOSION, objp-Objects, 0, F2_0, F1_0*512);
  2066. }
  2067. }
  2068. #ifndef SHAREWARE
  2069. #ifdef NETWORK
  2070. // --------------------------------------------------------------------------------------------------------------------
  2071. // Called for an AI object if it is fairly aware of the player.
  2072. // awareness_level is in 0..100. Larger numbers indicate greater awareness (eg, 99 if firing at player).
  2073. // In a given frame, might not get called for an object, or might be called more than once.
  2074. // The fact that this routine is not called for a given object does not mean that object is not interested in the player.
  2075. // Objects are moved by physics, so they can move even if not interested in a player. However, if their velocity or
  2076. // orientation is changing, this routine will be called.
  2077. // Return value:
  2078. // 0 this player IS NOT allowed to move this robot.
  2079. // 1 this player IS allowed to move this robot.
  2080. int ai_multiplayer_awareness(object *objp, int awareness_level)
  2081. {
  2082. int rval=1;
  2083. #ifndef SHAREWARE
  2084. if (Game_mode & GM_MULTI) {
  2085. if (awareness_level == 0)
  2086. return 0;
  2087. rval = multi_can_move_robot(objp-Objects, awareness_level);
  2088. }
  2089. #endif
  2090. return rval;
  2091. }
  2092. #else
  2093. #define ai_multiplayer_awareness(a, b) 1
  2094. #endif
  2095. #else
  2096. #define ai_multiplayer_awareness(a, b) 1
  2097. #endif
  2098. #ifndef NDEBUG
  2099. fix Prev_boss_shields = -1;
  2100. #endif
  2101. // --------------------------------------------------------------------------------------------------------------------
  2102. // Do special stuff for a boss.
  2103. void do_boss_stuff(object *objp)
  2104. {
  2105. // New code, fixes stupid bug which meant boss never gated in robots if > 32767 seconds played.
  2106. if (Last_teleport_time > GameTime)
  2107. Last_teleport_time = GameTime;
  2108. if (Last_gate_time > GameTime)
  2109. Last_gate_time = GameTime;
  2110. #ifndef NDEBUG
  2111. if (objp->shields != Prev_boss_shields) {
  2112. mprintf((0, "Boss shields = %7.3f, object %i\n", f2fl(objp->shields), objp-Objects));
  2113. Prev_boss_shields = objp->shields;
  2114. }
  2115. #endif
  2116. if (!Boss_dying) {
  2117. if (objp->ctype.ai_info.CLOAKED == 1) {
  2118. if ((GameTime - Boss_cloak_start_time > BOSS_CLOAK_DURATION/3) && (Boss_cloak_end_time - GameTime > BOSS_CLOAK_DURATION/3) && (GameTime - Last_teleport_time > Boss_teleport_interval)) {
  2119. if (ai_multiplayer_awareness(objp, 98))
  2120. teleport_boss(objp);
  2121. } else if (Boss_hit_this_frame) {
  2122. Boss_hit_this_frame = 0;
  2123. Last_teleport_time -= Boss_teleport_interval/4;
  2124. }
  2125. if (GameTime > Boss_cloak_end_time)
  2126. objp->ctype.ai_info.CLOAKED = 0;
  2127. } else {
  2128. if ((GameTime - Boss_cloak_end_time > Boss_cloak_interval) || Boss_hit_this_frame) {
  2129. if (ai_multiplayer_awareness(objp, 95))
  2130. {
  2131. Boss_hit_this_frame = 0;
  2132. Boss_cloak_start_time = GameTime;
  2133. Boss_cloak_end_time = GameTime+Boss_cloak_duration;
  2134. objp->ctype.ai_info.CLOAKED = 1;
  2135. #ifndef SHAREWARE
  2136. #ifdef NETWORK
  2137. if (Game_mode & GM_MULTI)
  2138. multi_send_boss_actions(objp-Objects, 2, 0, 0);
  2139. #endif
  2140. #endif
  2141. }
  2142. }
  2143. }
  2144. } else
  2145. do_boss_dying_frame(objp);
  2146. }
  2147. #define BOSS_TO_PLAYER_GATE_DISTANCE (F1_0*150)
  2148. #ifndef SHAREWARE
  2149. // --------------------------------------------------------------------------------------------------------------------
  2150. // Do special stuff for a boss.
  2151. void do_super_boss_stuff(object *objp, fix dist_to_player, int player_visibility)
  2152. {
  2153. static int eclip_state = 0;
  2154. do_boss_stuff(objp);
  2155. // Only master player can cause gating to occur.
  2156. #ifdef NETWORK
  2157. if ((Game_mode & GM_MULTI) && !network_i_am_master())
  2158. return;
  2159. #endif
  2160. if ((dist_to_player < BOSS_TO_PLAYER_GATE_DISTANCE) || player_visibility || (Game_mode & GM_MULTI)) {
  2161. if (GameTime - Last_gate_time > Gate_interval/2) {
  2162. restart_effect(BOSS_ECLIP_NUM);
  2163. #ifndef SHAREWARE
  2164. #ifdef NETWORK
  2165. if (eclip_state == 0) {
  2166. multi_send_boss_actions(objp-Objects, 4, 0, 0);
  2167. eclip_state = 1;
  2168. }
  2169. #endif
  2170. #endif
  2171. }
  2172. else {
  2173. stop_effect(BOSS_ECLIP_NUM);
  2174. #ifndef SHAREWARE
  2175. #ifdef NETWORK
  2176. if (eclip_state == 1) {
  2177. multi_send_boss_actions(objp-Objects, 5, 0, 0);
  2178. eclip_state = 0;
  2179. }
  2180. #endif
  2181. #endif
  2182. }
  2183. if (GameTime - Last_gate_time > Gate_interval)
  2184. if (ai_multiplayer_awareness(objp, 99)) {
  2185. int rtval;
  2186. int randtype = (rand() * MAX_GATE_INDEX) >> 15;
  2187. Assert(randtype < MAX_GATE_INDEX);
  2188. randtype = Super_boss_gate_list[randtype];
  2189. Assert(randtype < N_robot_types);
  2190. rtval = gate_in_robot(randtype, -1);
  2191. #ifndef SHAREWARE
  2192. #ifdef NETWORK
  2193. if (rtval && (Game_mode & GM_MULTI))
  2194. {
  2195. multi_send_boss_actions(objp-Objects, 3, randtype, Net_create_objnums[0]);
  2196. map_objnum_local_to_local(Net_create_objnums[0]);
  2197. }
  2198. #endif
  2199. #endif
  2200. }
  2201. }
  2202. }
  2203. #endif
  2204. //int multi_can_move_robot(object *objp, int awareness_level)
  2205. //{
  2206. // return 0;
  2207. //}
  2208. #ifndef SHAREWARE
  2209. void ai_multi_send_robot_position(int objnum, int force)
  2210. {
  2211. #ifndef SHAREWARE
  2212. #ifdef NETWORK
  2213. if (Game_mode & GM_MULTI)
  2214. {
  2215. if (force != -1)
  2216. multi_send_robot_position(objnum, 1);
  2217. else
  2218. multi_send_robot_position(objnum, 0);
  2219. }
  2220. #endif
  2221. #endif
  2222. return;
  2223. }
  2224. #else
  2225. #define ai_multi_send_robot_position(a, b)
  2226. #endif
  2227. // --------------------------------------------------------------------------------------------------------------------
  2228. // Returns true if this object should be allowed to fire at the player.
  2229. int maybe_ai_do_actual_firing_stuff(object *obj, ai_static *aip)
  2230. {
  2231. if (Game_mode & GM_MULTI)
  2232. if ((aip->GOAL_STATE != AIS_FLIN) && (obj->id != ROBOT_BRAIN))
  2233. if (aip->CURRENT_STATE == AIS_FIRE)
  2234. return 1;
  2235. return 0;
  2236. }
  2237. // --------------------------------------------------------------------------------------------------------------------
  2238. void ai_do_actual_firing_stuff(object *obj, ai_static *aip, ai_local *ailp, robot_info *robptr, vms_vector *vec_to_player, fix dist_to_player, vms_vector *gun_point, int player_visibility, int object_animates)
  2239. {
  2240. fix dot;
  2241. if (player_visibility == 2) {
  2242. // Changed by mk, 01/04/94, onearm would take about 9 seconds until he can fire at you.
  2243. // if (((!object_animates) || (ailp->achieved_state[aip->CURRENT_GUN] == AIS_FIRE)) && (ailp->next_fire <= 0)) {
  2244. if (!object_animates || (ailp->next_fire <= 0)) {
  2245. dot = vm_vec_dot(&obj->orient.fvec, vec_to_player);
  2246. if (dot >= 7*F1_0/8) {
  2247. if (aip->CURRENT_GUN < Robot_info[obj->id].n_guns) {
  2248. if (robptr->attack_type == 1) {
  2249. if (!Player_exploded && (dist_to_player < obj->size + ConsoleObject->size + F1_0*2)) { // robptr->circle_distance[Difficulty_level] + ConsoleObject->size) {
  2250. if (!ai_multiplayer_awareness(obj, ROBOT_FIRE_AGITATION-2))
  2251. return;
  2252. do_ai_robot_hit_attack(obj, ConsoleObject, &obj->pos);
  2253. } else {
  2254. // mprintf((0, "Green won't fire: Too far: dist = %7.3f, threshold = %7.3f\n", f2fl(dist_to_player), f2fl(obj->size + ConsoleObject->size + F1_0*2)));
  2255. return;
  2256. }
  2257. } else {
  2258. if ((gun_point->x == 0) && (gun_point->y == 0) && (gun_point->z == 0)) {
  2259. ; //mprintf((0, "Would like to fire gun, but gun not selected.\n"));
  2260. } else {
  2261. if (!ai_multiplayer_awareness(obj, ROBOT_FIRE_AGITATION))
  2262. return;
  2263. ai_fire_laser_at_player(obj, gun_point);
  2264. }
  2265. }
  2266. // Wants to fire, so should go into chase mode, probably.
  2267. if ( (aip->behavior != AIB_RUN_FROM) && (aip->behavior != AIB_STILL) && (aip->behavior != AIB_FOLLOW_PATH) && ((ailp->mode == AIM_FOLLOW_PATH) || (ailp->mode == AIM_STILL)))
  2268. ailp->mode = AIM_CHASE_OBJECT;
  2269. }
  2270. aip->GOAL_STATE = AIS_RECO;
  2271. ailp->goal_state[aip->CURRENT_GUN] = AIS_RECO;
  2272. // Switch to next gun for next fire.
  2273. aip->CURRENT_GUN++;
  2274. if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
  2275. aip->CURRENT_GUN = 0;
  2276. }
  2277. }
  2278. } else if (Weapon_info[Robot_info[obj->id].weapon_type].homing_flag == 1) {
  2279. // Robots which fire homing weapons might fire even if they don't have a bead on the player.
  2280. if (((!object_animates) || (ailp->achieved_state[aip->CURRENT_GUN] == AIS_FIRE)) && (ailp->next_fire <= 0) && (vm_vec_dist_quick(&Hit_pos, &obj->pos) > F1_0*40)) {
  2281. if (!ai_multiplayer_awareness(obj, ROBOT_FIRE_AGITATION))
  2282. return;
  2283. ai_fire_laser_at_player(obj, gun_point);
  2284. aip->GOAL_STATE = AIS_RECO;
  2285. ailp->goal_state[aip->CURRENT_GUN] = AIS_RECO;
  2286. // Switch to next gun for next fire.
  2287. aip->CURRENT_GUN++;
  2288. if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
  2289. aip->CURRENT_GUN = 0;
  2290. } else {
  2291. // Switch to next gun for next fire.
  2292. aip->CURRENT_GUN++;
  2293. if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
  2294. aip->CURRENT_GUN = 0;
  2295. }
  2296. }
  2297. }
  2298. // --------------------------------------------------------------------------------------------------------------------
  2299. void do_ai_frame(object *obj)
  2300. {
  2301. int objnum = obj-Objects;
  2302. ai_static *aip = &obj->ctype.ai_info;
  2303. ai_local *ailp = &Ai_local_info[objnum];
  2304. fix dist_to_player;
  2305. vms_vector vec_to_player;
  2306. fix dot;
  2307. robot_info *robptr;
  2308. int player_visibility=-1;
  2309. int obj_ref;
  2310. int object_animates;
  2311. int new_goal_state;
  2312. int visibility_and_vec_computed = 0;
  2313. int previous_visibility;
  2314. vms_vector gun_point;
  2315. vms_vector vis_vec_pos;
  2316. if (aip->SKIP_AI_COUNT) {
  2317. aip->SKIP_AI_COUNT--;
  2318. return;
  2319. }
  2320. // Kind of a hack. If a robot is flinching, but it is time for it to fire, unflinch it.
  2321. // Else, you can turn a big nasty robot into a wimp by firing flares at it.
  2322. // This also allows the player to see the cool flinch effect for mechs without unbalancing the game.
  2323. if ((aip->GOAL_STATE == AIS_FLIN) && (ailp->next_fire < 0)) {
  2324. aip->GOAL_STATE = AIS_FIRE;
  2325. }
  2326. #ifndef NDEBUG
  2327. if ((aip->behavior == AIB_RUN_FROM) && (ailp->mode != AIM_RUN_FROM_OBJECT))
  2328. Int3(); // This is peculiar. Behavior is run from, but mode is not. Contact Mike.
  2329. mprintf_animation_info((obj));
  2330. if (Ai_animation_test) {
  2331. if (aip->GOAL_STATE == aip->CURRENT_STATE) {
  2332. aip->GOAL_STATE++;
  2333. if (aip->GOAL_STATE > AIS_RECO)
  2334. aip->GOAL_STATE = AIS_REST;
  2335. }
  2336. mprintf((0, "Frame %4i, current = %i, goal = %i\n", FrameCount, aip->CURRENT_STATE, aip->GOAL_STATE));
  2337. object_animates = do_silly_animation(obj);
  2338. if (object_animates)
  2339. ai_frame_animation(obj);
  2340. return;
  2341. }
  2342. if (!Do_ai_flag)
  2343. return;
  2344. if (Break_on_object != -1)
  2345. if ((obj-Objects) == Break_on_object)
  2346. Int3(); // Contact Mike: This is a debug break
  2347. #endif
  2348. Believed_player_pos = Ai_cloak_info[objnum & (MAX_AI_CLOAK_INFO-1)].last_position;
  2349. // mprintf((0, "Object %i: behavior = %02x, mode = %i, awareness = %i, time = %7.3f\n", obj-Objects, aip->behavior, ailp->mode, ailp->player_awareness_type, f2fl(ailp->player_awareness_time)));
  2350. // mprintf((0, "Object %i: behavior = %02x, mode = %i, awareness = %i, cur=%i, goal=%i\n", obj-Objects, aip->behavior, ailp->mode, ailp->player_awareness_type, aip->CURRENT_STATE, aip->GOAL_STATE));
  2351. // Assert((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR));
  2352. if (!((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR))) {
  2353. // mprintf((0, "Object %i behavior is %i, setting to AIB_NORMAL, fix in editor!\n", objnum, aip->behavior));
  2354. aip->behavior = AIB_NORMAL;
  2355. }
  2356. Assert(obj->segnum != -1);
  2357. Assert(obj->id < N_robot_types);
  2358. robptr = &Robot_info[obj->id];
  2359. Assert(robptr->always_0xabcd == 0xabcd);
  2360. obj_ref = objnum ^ FrameCount;
  2361. // -- if (ailp->wait_time > -F1_0*8)
  2362. // -- ailp->wait_time -= FrameTime;
  2363. if (ailp->next_fire > -F1_0*8)
  2364. ailp->next_fire -= FrameTime;
  2365. if (ailp->time_since_processed < F1_0*256)
  2366. ailp->time_since_processed += FrameTime;
  2367. previous_visibility = ailp->previous_visibility; // Must get this before we toast the master copy!
  2368. // Deal with cloaking for robots which are cloaked except just before firing.
  2369. if (robptr->cloak_type == RI_CLOAKED_EXCEPT_FIRING)
  2370. if (ailp->next_fire < F1_0/2)
  2371. aip->CLOAKED = 1;
  2372. else
  2373. aip->CLOAKED = 0;
  2374. if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
  2375. Believed_player_pos = ConsoleObject->pos;
  2376. dist_to_player = vm_vec_dist_quick(&Believed_player_pos, &obj->pos);
  2377. //--!-- mprintf((0, "%2i: %s, [vel = %5.1f %5.1f %5.1f] spd = %4.1f dtp=%5.1f ", objnum, mode_text[ailp->mode], f2fl(obj->mtype.phys_info.velocity.x), f2fl(obj->mtype.phys_info.velocity.y), f2fl(obj->mtype.phys_info.velocity.z), f2fl(vm_vec_mag(&obj->mtype.phys_info.velocity)), f2fl(dist_to_player)));
  2378. //--!-- if (ailp->mode == AIM_FOLLOW_PATH) {
  2379. //--!-- mprintf((0, "gseg = %i\n", Point_segs[aip->hide_index+aip->cur_path_index].segnum));
  2380. //--!-- } else
  2381. //--!-- mprintf((0, "\n"));
  2382. // If this robot can fire, compute visibility from gun position.
  2383. // Don't want to compute visibility twice, as it is expensive. (So is call to calc_gun_point).
  2384. if ((ailp->next_fire <= 0) && (dist_to_player < F1_0*200) && (robptr->n_guns) && !(robptr->attack_type)) {
  2385. calc_gun_point(&gun_point, obj, aip->CURRENT_GUN);
  2386. vis_vec_pos = gun_point;
  2387. // mprintf((0, "Visibility = %i, computed from gun #%i\n", player_visibility, aip->CURRENT_GUN));
  2388. } else {
  2389. vis_vec_pos = obj->pos;
  2390. vm_vec_zero(&gun_point);
  2391. // mprintf((0, "Visibility = %i, computed from center.\n", player_visibility));
  2392. }
  2393. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2394. // Occasionally make non-still robots make a path to the player. Based on agitation and distance from player.
  2395. if ((aip->behavior != AIB_RUN_FROM) && (aip->behavior != AIB_STILL) && !(Game_mode & GM_MULTI))
  2396. if (Overall_agitation > 70) {
  2397. if ((dist_to_player < F1_0*200) && (rand() < FrameTime/4)) {
  2398. if (rand() * (Overall_agitation - 40) > F1_0*5) {
  2399. // -- mprintf((0, "(1) Object #%i going from still to path in frame %i.\n", objnum, FrameCount));
  2400. create_path_to_player(obj, 4 + Overall_agitation/8 + Difficulty_level, 1);
  2401. // -- show_path_and_other(obj);
  2402. return;
  2403. }
  2404. }
  2405. }
  2406. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2407. // If retry count not 0, then add it into consecutive_retries.
  2408. // If it is 0, cut down consecutive_retries.
  2409. // This is largely a hack to speed up physics and deal with stupid AI. This is low level
  2410. // communication between systems of a sort that should not be done.
  2411. if ((ailp->retry_count) && !(Game_mode & GM_MULTI)) {
  2412. ailp->consecutive_retries += ailp->retry_count;
  2413. ailp->retry_count = 0;
  2414. if (ailp->consecutive_retries > 3) {
  2415. switch (ailp->mode) {
  2416. case AIM_CHASE_OBJECT:
  2417. // -- mprintf((0, "(2) Object #%i, retries while chasing, creating path to player in frame %i\n", objnum, FrameCount));
  2418. create_path_to_player(obj, 4 + Overall_agitation/8 + Difficulty_level, 1);
  2419. break;
  2420. case AIM_STILL:
  2421. if (!((aip->behavior == AIB_STILL) || (aip->behavior == AIB_STATION))) // Behavior is still, so don't follow path.
  2422. attempt_to_resume_path(obj);
  2423. break;
  2424. case AIM_FOLLOW_PATH:
  2425. // mprintf((0, "Object %i following path got %i retries in frame %i\n", obj-Objects, ailp->consecutive_retries, FrameCount));
  2426. if (Game_mode & GM_MULTI)
  2427. ailp->mode = AIM_STILL;
  2428. else
  2429. attempt_to_resume_path(obj);
  2430. break;
  2431. case AIM_RUN_FROM_OBJECT:
  2432. move_towards_segment_center(obj);
  2433. obj->mtype.phys_info.velocity.x = 0;
  2434. obj->mtype.phys_info.velocity.y = 0;
  2435. obj->mtype.phys_info.velocity.z = 0;
  2436. create_n_segment_path(obj, 5, -1);
  2437. ailp->mode = AIM_RUN_FROM_OBJECT;
  2438. break;
  2439. case AIM_HIDE:
  2440. move_towards_segment_center(obj);
  2441. obj->mtype.phys_info.velocity.x = 0;
  2442. obj->mtype.phys_info.velocity.y = 0;
  2443. obj->mtype.phys_info.velocity.z = 0;
  2444. // -- mprintf((0, "Hiding, yet creating path to player.\n"));
  2445. if (Overall_agitation > (50 - Difficulty_level*4))
  2446. create_path_to_player(obj, 4 + Overall_agitation/8, 1);
  2447. else {
  2448. create_n_segment_path(obj, 5, -1);
  2449. }
  2450. break;
  2451. case AIM_OPEN_DOOR:
  2452. create_n_segment_path_to_door(obj, 5, -1);
  2453. break;
  2454. #ifndef NDEBUG
  2455. case AIM_FOLLOW_PATH_2:
  2456. Int3(); // Should never happen!
  2457. break;
  2458. #endif
  2459. }
  2460. ailp->consecutive_retries = 0;
  2461. }
  2462. } else
  2463. ailp->consecutive_retries /= 2;
  2464. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2465. // If in materialization center, exit
  2466. if (!(Game_mode & GM_MULTI) && (Segments[obj->segnum].special == SEGMENT_IS_ROBOTMAKER)) {
  2467. ai_follow_path(obj, 1); // 1 = player is visible, which might be a lie, but it works.
  2468. return;
  2469. }
  2470. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2471. // Decrease player awareness due to the passage of time.
  2472. //-----old, faster way from about 11/06/94----- if (ailp->player_awareness_type) {
  2473. //-----old, faster way from about 11/06/94----- if (ailp->player_awareness_time > 0) {
  2474. //-----old, faster way from about 11/06/94----- ailp->player_awareness_time -= FrameTime;
  2475. //-----old, faster way from about 11/06/94----- if (ailp->player_awareness_time <= 0) {
  2476. //-----old, faster way from about 11/06/94----- ailp->player_awareness_time = F1_0*2;
  2477. //-----old, faster way from about 11/06/94----- ailp->player_awareness_type--;
  2478. //-----old, faster way from about 11/06/94----- if (ailp->player_awareness_type < 0) {
  2479. //-----old, faster way from about 11/06/94----- aip->GOAL_STATE = AIS_REST;
  2480. //-----old, faster way from about 11/06/94----- ailp->player_awareness_type = 0;
  2481. //-----old, faster way from about 11/06/94----- }
  2482. //-----old, faster way from about 11/06/94-----
  2483. //-----old, faster way from about 11/06/94----- }
  2484. //-----old, faster way from about 11/06/94----- } else {
  2485. //-----old, faster way from about 11/06/94----- ailp->player_awareness_type = 0;
  2486. //-----old, faster way from about 11/06/94----- aip->GOAL_STATE = AIS_REST;
  2487. //-----old, faster way from about 11/06/94----- }
  2488. //-----old, faster way from about 11/06/94----- } else
  2489. //-----old, faster way from about 11/06/94----- aip->GOAL_STATE = AIS_REST;
  2490. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2491. // Decrease player awareness due to the passage of time.
  2492. if (ailp->player_awareness_type) {
  2493. if (ailp->player_awareness_time > 0) {
  2494. ailp->player_awareness_time -= FrameTime;
  2495. if (ailp->player_awareness_time <= 0) {
  2496. ailp->player_awareness_time = F1_0*2; //new: 11/05/94
  2497. ailp->player_awareness_type--; //new: 11/05/94
  2498. }
  2499. } else {
  2500. ailp->player_awareness_type--;
  2501. ailp->player_awareness_time = F1_0*2;
  2502. // aip->GOAL_STATE = AIS_REST;
  2503. }
  2504. } else
  2505. aip->GOAL_STATE = AIS_REST; //new: 12/13/94
  2506. if (Player_is_dead && (ailp->player_awareness_type == 0))
  2507. if ((dist_to_player < F1_0*200) && (rand() < FrameTime/8)) {
  2508. if ((aip->behavior != AIB_STILL) && (aip->behavior != AIB_RUN_FROM)) {
  2509. if (!ai_multiplayer_awareness(obj, 30))
  2510. return;
  2511. ai_multi_send_robot_position(objnum, -1);
  2512. if (!((ailp->mode == AIM_FOLLOW_PATH) && (aip->cur_path_index < aip->path_length-1)))
  2513. if (dist_to_player < F1_0*30)
  2514. create_n_segment_path(obj, 5, 1);
  2515. else
  2516. create_path_to_player(obj, 20, 1);
  2517. }
  2518. }
  2519. // Make sure that if this guy got hit or bumped, then he's chasing player.
  2520. if ((ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION) || (ailp->player_awareness_type >= PA_PLAYER_COLLISION)) {
  2521. if ((aip->behavior != AIB_STILL) && (aip->behavior != AIB_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM) && (obj->id != ROBOT_BRAIN))
  2522. ailp->mode = AIM_CHASE_OBJECT;
  2523. }
  2524. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2525. if ((aip->GOAL_STATE == AIS_FLIN) && (aip->CURRENT_STATE == AIS_FLIN))
  2526. aip->GOAL_STATE = AIS_LOCK;
  2527. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2528. // Note: Should only do these two function calls for objects which animate
  2529. if ((dist_to_player < F1_0*100)) { // && !(Game_mode & GM_MULTI)) {
  2530. object_animates = do_silly_animation(obj);
  2531. if (object_animates)
  2532. ai_frame_animation(obj);
  2533. //mprintf((0, "Object %i: goal=%i, current=%i\n", obj-Objects, obj->ctype.ai_info.GOAL_STATE, obj->ctype.ai_info.CURRENT_STATE));
  2534. } else {
  2535. // If Object is supposed to animate, but we don't let it animate due to distance, then
  2536. // we must change its state, else it will never update.
  2537. aip->CURRENT_STATE = aip->GOAL_STATE;
  2538. object_animates = 0; // If we're not doing the animation, then should pretend it doesn't animate.
  2539. }
  2540. //Processed_this_frame++;
  2541. //if (FrameCount != LastFrameCount) {
  2542. // LastFrameCount = FrameCount;
  2543. // mprintf((0, "Processed in frame %i = %i robots\n", FrameCount-1, Processed_this_frame));
  2544. // Processed_this_frame = 0;
  2545. //}
  2546. switch (Robot_info[obj->id].boss_flag) {
  2547. case 0:
  2548. break;
  2549. case 1:
  2550. if (aip->GOAL_STATE == AIS_FLIN)
  2551. aip->GOAL_STATE = AIS_FIRE;
  2552. if (aip->CURRENT_STATE == AIS_FLIN)
  2553. aip->CURRENT_STATE = AIS_FIRE;
  2554. dist_to_player /= 4;
  2555. do_boss_stuff(obj);
  2556. dist_to_player *= 4;
  2557. break;
  2558. #ifndef SHAREWARE
  2559. case 2:
  2560. if (aip->GOAL_STATE == AIS_FLIN)
  2561. aip->GOAL_STATE = AIS_FIRE;
  2562. if (aip->CURRENT_STATE == AIS_FLIN)
  2563. aip->CURRENT_STATE = AIS_FIRE;
  2564. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2565. { int pv = player_visibility;
  2566. fix dtp = dist_to_player/4;
  2567. // If player cloaked, visibility is screwed up and superboss will gate in robots when not supposed to.
  2568. if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
  2569. pv = 0;
  2570. dtp = vm_vec_dist_quick(&ConsoleObject->pos, &obj->pos)/4;
  2571. }
  2572. do_super_boss_stuff(obj, dtp, pv);
  2573. }
  2574. break;
  2575. #endif
  2576. default:
  2577. Int3(); // Bogus boss flag value.
  2578. }
  2579. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2580. // Time-slice, don't process all the time, purely an efficiency hack.
  2581. // Guys whose behavior is station and are not at their hide segment get processed anyway.
  2582. if (ailp->player_awareness_type < PA_WEAPON_ROBOT_COLLISION-1) { // If robot got hit, he gets to attack player always!
  2583. #ifndef NDEBUG
  2584. if (Break_on_object != objnum) { // don't time slice if we're interested in this object.
  2585. #endif
  2586. if ((dist_to_player > F1_0*250) && (ailp->time_since_processed <= F1_0*2))
  2587. return;
  2588. else if (!((aip->behavior == AIB_STATION) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->hide_segment != obj->segnum))) {
  2589. if ((dist_to_player > F1_0*150) && (ailp->time_since_processed <= F1_0))
  2590. return;
  2591. else if ((dist_to_player > F1_0*100) && (ailp->time_since_processed <= F1_0/2))
  2592. return;
  2593. }
  2594. #ifndef NDEBUG
  2595. }
  2596. #endif
  2597. }
  2598. // -- if ((aip->behavior == AIB_STATION) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->hide_segment != obj->segnum))
  2599. // -- mprintf((0, "[%i] ", obj-Objects));
  2600. // Reset time since processed, but skew objects so not everything processed synchronously, else
  2601. // we get fast frames with the occasional very slow frame.
  2602. // AI_proc_time = ailp->time_since_processed;
  2603. ailp->time_since_processed = - ((objnum & 0x03) * FrameTime ) / 2;
  2604. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2605. // Perform special ability
  2606. switch (obj->id) {
  2607. case ROBOT_BRAIN:
  2608. // Robots function nicely if behavior is Station. This means they won't move until they
  2609. // can see the player, at which time they will start wandering about opening doors.
  2610. if (ConsoleObject->segnum == obj->segnum) {
  2611. if (!ai_multiplayer_awareness(obj, 97))
  2612. return;
  2613. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2614. move_away_from_player(obj, &vec_to_player, 0);
  2615. ai_multi_send_robot_position(objnum, -1);
  2616. } else if (ailp->mode != AIM_STILL) {
  2617. int r;
  2618. r = openable_doors_in_segment(obj);
  2619. if (r != -1) {
  2620. ailp->mode = AIM_OPEN_DOOR;
  2621. aip->GOALSIDE = r;
  2622. } else if (ailp->mode != AIM_FOLLOW_PATH) {
  2623. if (!ai_multiplayer_awareness(obj, 50))
  2624. return;
  2625. create_n_segment_path_to_door(obj, 8+Difficulty_level, -1); // third parameter is avoid_seg, -1 means avoid nothing.
  2626. ai_multi_send_robot_position(objnum, -1);
  2627. }
  2628. } else {
  2629. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2630. if (player_visibility) {
  2631. if (!ai_multiplayer_awareness(obj, 50))
  2632. return;
  2633. create_n_segment_path_to_door(obj, 8+Difficulty_level, -1); // third parameter is avoid_seg, -1 means avoid nothing.
  2634. ai_multi_send_robot_position(objnum, -1);
  2635. }
  2636. }
  2637. break;
  2638. default:
  2639. break;
  2640. }
  2641. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2642. switch (ailp->mode) {
  2643. case AIM_CHASE_OBJECT: { // chasing player, sort of, chase if far, back off if close, circle in between
  2644. fix circle_distance;
  2645. circle_distance = robptr->circle_distance[Difficulty_level] + ConsoleObject->size;
  2646. // Green guy doesn't get his circle distance boosted, else he might never attack.
  2647. if (robptr->attack_type != 1)
  2648. circle_distance += (objnum&0xf) * F1_0/2;
  2649. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2650. // @mk, 12/27/94, structure here was strange. Would do both clauses of what are now this if/then/else. Used to be if/then, if/then.
  2651. if ((player_visibility < 2) && (previous_visibility == 2)) { // this is redundant: mk, 01/15/95: && (ailp->mode == AIM_CHASE_OBJECT)) {
  2652. // mprintf((0, "I used to be able to see the player!\n"));
  2653. if (!ai_multiplayer_awareness(obj, 53)) {
  2654. if (maybe_ai_do_actual_firing_stuff(obj, aip))
  2655. ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  2656. return;
  2657. }
  2658. // -- mprintf((0, "(3) Object #%i going from chase to player path in frame %i.\n", objnum, FrameCount));
  2659. create_path_to_player(obj, 8, 1);
  2660. // -- show_path_and_other(obj);
  2661. ai_multi_send_robot_position(objnum, -1);
  2662. } else if ((player_visibility == 0) && (dist_to_player > F1_0*80) && (!(Game_mode & GM_MULTI))) {
  2663. // If pretty far from the player, player cannot be seen (obstructed) and in chase mode, switch to follow path mode.
  2664. // This has one desirable benefit of avoiding physics retries.
  2665. if (aip->behavior == AIB_STATION) {
  2666. ailp->goal_segment = aip->hide_segment;
  2667. // -- mprintf((0, "(1) Object #%i going from chase to STATION in frame %i.\n", objnum, FrameCount));
  2668. create_path_to_station(obj, 15);
  2669. // -- show_path_and_other(obj);
  2670. } else
  2671. create_n_segment_path(obj, 5, -1);
  2672. break;
  2673. }
  2674. if ((aip->CURRENT_STATE == AIS_REST) && (aip->GOAL_STATE == AIS_REST)) {
  2675. if (player_visibility) {
  2676. if (rand() < FrameTime*player_visibility) {
  2677. if (dist_to_player/256 < rand()*player_visibility) {
  2678. // mprintf((0, "Object %i searching for player.\n", obj-Objects));
  2679. aip->GOAL_STATE = AIS_SRCH;
  2680. aip->CURRENT_STATE = AIS_SRCH;
  2681. }
  2682. }
  2683. }
  2684. }
  2685. if (GameTime - ailp->time_player_seen > CHASE_TIME_LENGTH) {
  2686. if (Game_mode & GM_MULTI)
  2687. if (!player_visibility && (dist_to_player > F1_0*70)) {
  2688. ailp->mode = AIM_STILL;
  2689. return;
  2690. }
  2691. if (!ai_multiplayer_awareness(obj, 64)) {
  2692. if (maybe_ai_do_actual_firing_stuff(obj, aip))
  2693. ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  2694. return;
  2695. }
  2696. // -- mprintf((0, "(4) Object #%i going from chase to player path in frame %i.\n", objnum, FrameCount));
  2697. create_path_to_player(obj, 10, 1);
  2698. // -- show_path_and_other(obj);
  2699. ai_multi_send_robot_position(objnum, -1);
  2700. } else if ((aip->CURRENT_STATE != AIS_REST) && (aip->GOAL_STATE != AIS_REST)) {
  2701. if (!ai_multiplayer_awareness(obj, 70)) {
  2702. if (maybe_ai_do_actual_firing_stuff(obj, aip))
  2703. ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  2704. return;
  2705. }
  2706. ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, circle_distance, 0);
  2707. if ((obj_ref & 1) && ((aip->GOAL_STATE == AIS_SRCH) || (aip->GOAL_STATE == AIS_LOCK))) {
  2708. if (player_visibility) // == 2)
  2709. ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  2710. else
  2711. ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  2712. }
  2713. if (ai_evaded) {
  2714. ai_multi_send_robot_position(objnum, 1);
  2715. ai_evaded = 0;
  2716. }
  2717. else
  2718. ai_multi_send_robot_position(objnum, -1);
  2719. do_firing_stuff(obj, player_visibility, &vec_to_player);
  2720. }
  2721. break;
  2722. }
  2723. case AIM_RUN_FROM_OBJECT:
  2724. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2725. if (player_visibility) {
  2726. if (ailp->player_awareness_type == 0)
  2727. ailp->player_awareness_type = PA_WEAPON_ROBOT_COLLISION;
  2728. }
  2729. // If in multiplayer, only do if player visible. If not multiplayer, do always.
  2730. if (!(Game_mode & GM_MULTI) || player_visibility)
  2731. if (ai_multiplayer_awareness(obj, 75)) {
  2732. ai_follow_path(obj, player_visibility);
  2733. ai_multi_send_robot_position(objnum, -1);
  2734. }
  2735. if (aip->GOAL_STATE != AIS_FLIN)
  2736. aip->GOAL_STATE = AIS_LOCK;
  2737. else if (aip->CURRENT_STATE == AIS_FLIN)
  2738. aip->GOAL_STATE = AIS_LOCK;
  2739. // Bad to let run_from robot fire at player because it will cause a war in which it turns towards the
  2740. // player to fire and then towards its goal to move.
  2741. // do_firing_stuff(obj, player_visibility, &vec_to_player);
  2742. // Instead, do this:
  2743. // (Note, only drop if player is visible. This prevents the bombs from being a giveaway, and
  2744. // also ensures that the robot is moving while it is dropping. Also means fewer will be dropped.)
  2745. if ((ailp->next_fire <= 0) && (player_visibility)) {
  2746. vms_vector fire_vec, fire_pos;
  2747. if (!ai_multiplayer_awareness(obj, 75))
  2748. return;
  2749. fire_vec = obj->orient.fvec;
  2750. vm_vec_negate(&fire_vec);
  2751. vm_vec_add(&fire_pos, &obj->pos, &fire_vec);
  2752. Laser_create_new_easy( &fire_vec, &fire_pos, obj-Objects, PROXIMITY_ID, 1);
  2753. ailp->next_fire = F1_0*5; // Drop a proximity bomb every 5 seconds.
  2754. #ifdef NETWORK
  2755. if (Game_mode & GM_MULTI)
  2756. {
  2757. ai_multi_send_robot_position(obj-Objects, -1);
  2758. multi_send_robot_fire(obj-Objects, -1, &fire_vec);
  2759. }
  2760. #endif
  2761. }
  2762. break;
  2763. case AIM_FOLLOW_PATH: {
  2764. int anger_level = 65;
  2765. if (aip->behavior == AIB_STATION)
  2766. if (Point_segs[aip->hide_index + aip->path_length - 1].segnum == aip->hide_segment) {
  2767. anger_level = 64;
  2768. // mprintf((0, "Object %i, station, lowering anger to 64.\n"));
  2769. }
  2770. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2771. if (Game_mode & (GM_MODEM | GM_SERIAL))
  2772. if (!player_visibility && (dist_to_player > F1_0*70)) {
  2773. ailp->mode = AIM_STILL;
  2774. return;
  2775. }
  2776. if (!ai_multiplayer_awareness(obj, anger_level)) {
  2777. if (maybe_ai_do_actual_firing_stuff(obj, aip)) {
  2778. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2779. ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  2780. }
  2781. return;
  2782. }
  2783. ai_follow_path(obj, player_visibility);
  2784. if (aip->GOAL_STATE != AIS_FLIN)
  2785. aip->GOAL_STATE = AIS_LOCK;
  2786. else if (aip->CURRENT_STATE == AIS_FLIN)
  2787. aip->GOAL_STATE = AIS_LOCK;
  2788. if ((aip->behavior != AIB_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM))
  2789. do_firing_stuff(obj, player_visibility, &vec_to_player);
  2790. if ((player_visibility == 2) && (aip->behavior != AIB_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM) && (obj->id != ROBOT_BRAIN)) {
  2791. if (robptr->attack_type == 0)
  2792. ailp->mode = AIM_CHASE_OBJECT;
  2793. } else if ((player_visibility == 0) && (aip->behavior == AIB_NORMAL) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM)) {
  2794. ailp->mode = AIM_STILL;
  2795. aip->hide_index = -1;
  2796. aip->path_length = 0;
  2797. }
  2798. ai_multi_send_robot_position(objnum, -1);
  2799. break;
  2800. }
  2801. case AIM_HIDE:
  2802. if (!ai_multiplayer_awareness(obj, 71)) {
  2803. if (maybe_ai_do_actual_firing_stuff(obj, aip)) {
  2804. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2805. ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  2806. }
  2807. return;
  2808. }
  2809. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2810. ai_follow_path(obj, player_visibility);
  2811. if (aip->GOAL_STATE != AIS_FLIN)
  2812. aip->GOAL_STATE = AIS_LOCK;
  2813. else if (aip->CURRENT_STATE == AIS_FLIN)
  2814. aip->GOAL_STATE = AIS_LOCK;
  2815. ai_multi_send_robot_position(objnum, -1);
  2816. break;
  2817. case AIM_STILL:
  2818. if ((dist_to_player < F1_0*120+Difficulty_level*F1_0*20) || (ailp->player_awareness_type >= PA_WEAPON_ROBOT_COLLISION-1)) {
  2819. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2820. // turn towards vector if visible this time or last time, or rand
  2821. // new!
  2822. if ((player_visibility) || (previous_visibility) || ((rand() > 0x4000) && !(Game_mode & GM_MULTI))) {
  2823. if (!ai_multiplayer_awareness(obj, 71)) {
  2824. if (maybe_ai_do_actual_firing_stuff(obj, aip))
  2825. ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  2826. return;
  2827. }
  2828. ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  2829. ai_multi_send_robot_position(objnum, -1);
  2830. }
  2831. do_firing_stuff(obj, player_visibility, &vec_to_player);
  2832. // This is debugging code! Remove it! It's to make the green guy attack without doing other kinds of movement.
  2833. if (player_visibility) { // Change, MK, 01/03/94 for Multiplayer reasons. If robots can't see you (even with eyes on back of head), then don't do evasion.
  2834. if (robptr->attack_type == 1) {
  2835. aip->behavior = AIB_NORMAL;
  2836. if (!ai_multiplayer_awareness(obj, 80)) {
  2837. if (maybe_ai_do_actual_firing_stuff(obj, aip))
  2838. ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  2839. return;
  2840. }
  2841. ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, 0, 0);
  2842. if (ai_evaded) {
  2843. ai_multi_send_robot_position(objnum, 1);
  2844. ai_evaded = 0;
  2845. }
  2846. else
  2847. ai_multi_send_robot_position(objnum, -1);
  2848. } else {
  2849. // Robots in hover mode are allowed to evade at half normal speed.
  2850. if (!ai_multiplayer_awareness(obj, 81)) {
  2851. if (maybe_ai_do_actual_firing_stuff(obj, aip))
  2852. ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  2853. return;
  2854. }
  2855. ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, 0, 1);
  2856. if (ai_evaded) {
  2857. ai_multi_send_robot_position(objnum, -1);
  2858. ai_evaded = 0;
  2859. }
  2860. else
  2861. ai_multi_send_robot_position(objnum, -1);
  2862. }
  2863. } else if ((obj->segnum != aip->hide_segment) && (dist_to_player > F1_0*80) && (!(Game_mode & GM_MULTI))) {
  2864. // If pretty far from the player, player cannot be seen (obstructed) and in chase mode, switch to follow path mode.
  2865. // This has one desirable benefit of avoiding physics retries.
  2866. if (aip->behavior == AIB_STATION) {
  2867. ailp->goal_segment = aip->hide_segment;
  2868. // -- mprintf((0, "(2) Object #%i going from STILL to STATION in frame %i.\n", objnum, FrameCount));
  2869. create_path_to_station(obj, 15);
  2870. // -- show_path_and_other(obj);
  2871. }
  2872. break;
  2873. }
  2874. }
  2875. break;
  2876. case AIM_OPEN_DOOR: { // trying to open a door.
  2877. vms_vector center_point, goal_vector;
  2878. Assert(obj->id == ROBOT_BRAIN); // Make sure this guy is allowed to be in this mode.
  2879. if (!ai_multiplayer_awareness(obj, 62))
  2880. return;
  2881. compute_center_point_on_side(&center_point, &Segments[obj->segnum], aip->GOALSIDE);
  2882. vm_vec_sub(&goal_vector, &center_point, &obj->pos);
  2883. vm_vec_normalize_quick(&goal_vector);
  2884. ai_turn_towards_vector(&goal_vector, obj, robptr->turn_time[Difficulty_level]);
  2885. move_towards_vector(obj, &goal_vector);
  2886. ai_multi_send_robot_position(objnum, -1);
  2887. break;
  2888. }
  2889. default:
  2890. mprintf((0, "Unknown mode = %i in robot %i, behavior = %i\n", ailp->mode, obj-Objects, aip->behavior));
  2891. ailp->mode = AIM_CHASE_OBJECT;
  2892. break;
  2893. } // end: switch (ailp->mode) {
  2894. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2895. // If the robot can see you, increase his awareness of you.
  2896. // This prevents the problem of a robot looking right at you but doing nothing.
  2897. // Assert(player_visibility != -1); // Means it didn't get initialized!
  2898. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2899. if (player_visibility == 2)
  2900. if (ailp->player_awareness_type == 0)
  2901. ailp->player_awareness_type = PA_PLAYER_COLLISION;
  2902. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2903. if (!object_animates) {
  2904. aip->CURRENT_STATE = aip->GOAL_STATE;
  2905. // mprintf((0, "Setting current to goal (%i) because object doesn't animate.\n", aip->GOAL_STATE));
  2906. }
  2907. Assert(ailp->player_awareness_type <= AIE_MAX);
  2908. Assert(aip->CURRENT_STATE < AIS_MAX);
  2909. Assert(aip->GOAL_STATE < AIS_MAX);
  2910. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2911. if (ailp->player_awareness_type) {
  2912. new_goal_state = Ai_transition_table[ailp->player_awareness_type-1][aip->CURRENT_STATE][aip->GOAL_STATE];
  2913. if (ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION) {
  2914. // Decrease awareness, else this robot will flinch every frame.
  2915. ailp->player_awareness_type--;
  2916. ailp->player_awareness_time = F1_0*3;
  2917. }
  2918. if (new_goal_state == AIS_ERR_)
  2919. new_goal_state = AIS_REST;
  2920. if (aip->CURRENT_STATE == AIS_NONE)
  2921. aip->CURRENT_STATE = AIS_REST;
  2922. aip->GOAL_STATE = new_goal_state;
  2923. }
  2924. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2925. // If new state = fire, then set all gun states to fire.
  2926. if ((aip->GOAL_STATE == AIS_FIRE) ) {
  2927. int i,num_guns;
  2928. num_guns = Robot_info[obj->id].n_guns;
  2929. for (i=0; i<num_guns; i++)
  2930. ailp->goal_state[i] = AIS_FIRE;
  2931. }
  2932. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2933. // Hack by mk on 01/04/94, if a guy hasn't animated to the firing state, but his next_fire says ok to fire, bash him there
  2934. if ((ailp->next_fire < 0) && (aip->GOAL_STATE == AIS_FIRE))
  2935. aip->CURRENT_STATE = AIS_FIRE;
  2936. if ((aip->GOAL_STATE != AIS_FLIN) && (obj->id != ROBOT_BRAIN)) {
  2937. switch (aip->CURRENT_STATE) {
  2938. case AIS_NONE:
  2939. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2940. dot = vm_vec_dot(&obj->orient.fvec, &vec_to_player);
  2941. if (dot >= F1_0/2)
  2942. if (aip->GOAL_STATE == AIS_REST)
  2943. aip->GOAL_STATE = AIS_SRCH;
  2944. //mprintf((0, "State = none, goal = %i.\n", aip->GOAL_STATE));
  2945. break;
  2946. case AIS_REST:
  2947. if (aip->GOAL_STATE == AIS_REST) {
  2948. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2949. if ((ailp->next_fire <= 0) && (player_visibility)) {
  2950. // mprintf((0, "Setting goal state to fire from rest.\n"));
  2951. aip->GOAL_STATE = AIS_FIRE;
  2952. }
  2953. //mprintf((0, "State = rest, goal = %i.\n", aip->GOAL_STATE));
  2954. }
  2955. break;
  2956. case AIS_SRCH:
  2957. if (!ai_multiplayer_awareness(obj, 60))
  2958. return;
  2959. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2960. if (player_visibility) {
  2961. ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  2962. ai_multi_send_robot_position(objnum, -1);
  2963. } else if (!(Game_mode & GM_MULTI))
  2964. ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  2965. break;
  2966. case AIS_LOCK:
  2967. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2968. if (!(Game_mode & GM_MULTI) || (player_visibility)) {
  2969. if (!ai_multiplayer_awareness(obj, 68))
  2970. return;
  2971. if (player_visibility) {
  2972. ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  2973. ai_multi_send_robot_position(objnum, -1);
  2974. } else if (!(Game_mode & GM_MULTI))
  2975. ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  2976. }
  2977. break;
  2978. case AIS_FIRE:
  2979. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2980. if (player_visibility) {
  2981. if (!ai_multiplayer_awareness(obj, (ROBOT_FIRE_AGITATION-1)))
  2982. {
  2983. if (Game_mode & GM_MULTI) {
  2984. ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  2985. return;
  2986. }
  2987. }
  2988. ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  2989. ai_multi_send_robot_position(objnum, -1);
  2990. } else if (!(Game_mode & GM_MULTI)) {
  2991. ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  2992. }
  2993. // Fire at player, if appropriate.
  2994. ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  2995. break;
  2996. case AIS_RECO:
  2997. if (!(obj_ref & 3)) {
  2998. compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2999. if (player_visibility) {
  3000. if (!ai_multiplayer_awareness(obj, 69))
  3001. return;
  3002. ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  3003. ai_multi_send_robot_position(objnum, -1);
  3004. } else if (!(Game_mode & GM_MULTI)) {
  3005. ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  3006. }
  3007. }
  3008. break;
  3009. case AIS_FLIN:
  3010. // mprintf((0, "State = flinch, goal = %i.\n", aip->GOAL_STATE));
  3011. break;
  3012. default:
  3013. mprintf((1, "Unknown mode for AI object #%i\n", objnum));
  3014. aip->GOAL_STATE = AIS_REST;
  3015. aip->CURRENT_STATE = AIS_REST;
  3016. break;
  3017. }
  3018. } // end of: if (aip->GOAL_STATE != AIS_FLIN) {
  3019. // Switch to next gun for next fire.
  3020. if (player_visibility == 0) {
  3021. aip->CURRENT_GUN++;
  3022. if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
  3023. aip->CURRENT_GUN = 0;
  3024. }
  3025. }
  3026. //--mk, 121094 -- // ----------------------------------------------------------------------------------
  3027. //--mk, 121094 -- void spin_robot(object *robot, vms_vector *collision_point)
  3028. //--mk, 121094 -- {
  3029. //--mk, 121094 -- if (collision_point->x != 3) {
  3030. //--mk, 121094 -- robot->phys_info.rotvel.x = 0x1235;
  3031. //--mk, 121094 -- robot->phys_info.rotvel.y = 0x2336;
  3032. //--mk, 121094 -- robot->phys_info.rotvel.z = 0x3737;
  3033. //--mk, 121094 -- }
  3034. //--mk, 121094 --
  3035. //--mk, 121094 -- }
  3036. // -----------------------------------------------------------------------------------
  3037. void ai_do_cloak_stuff(void)
  3038. {
  3039. int i;
  3040. for (i=0; i<MAX_AI_CLOAK_INFO; i++) {
  3041. Ai_cloak_info[i].last_position = ConsoleObject->pos;
  3042. Ai_cloak_info[i].last_time = GameTime;
  3043. }
  3044. // Make work for control centers.
  3045. Believed_player_pos = Ai_cloak_info[0].last_position;
  3046. }
  3047. // -----------------------------------------------------------------------------------
  3048. // Returns false if awareness is considered too puny to add, else returns true.
  3049. int add_awareness_event(object *objp, int type)
  3050. {
  3051. // If player cloaked and hit a robot, then increase awareness
  3052. if ((type == PA_WEAPON_ROBOT_COLLISION) || (type == PA_WEAPON_WALL_COLLISION) || (type == PA_PLAYER_COLLISION))
  3053. ai_do_cloak_stuff();
  3054. if (Num_awareness_events < MAX_AWARENESS_EVENTS) {
  3055. if ((type == PA_WEAPON_WALL_COLLISION) || (type == PA_WEAPON_ROBOT_COLLISION))
  3056. if (objp->id == VULCAN_ID)
  3057. if (rand() > 3276)
  3058. return 0; // For vulcan cannon, only about 1/10 actually cause awareness
  3059. Awareness_events[Num_awareness_events].segnum = objp->segnum;
  3060. Awareness_events[Num_awareness_events].pos = objp->pos;
  3061. Awareness_events[Num_awareness_events].type = type;
  3062. Num_awareness_events++;
  3063. } else
  3064. Assert(0); // Hey -- Overflowed Awareness_events, make more or something
  3065. // This just gets ignored, so you can just continue.
  3066. return 1;
  3067. }
  3068. // ----------------------------------------------------------------------------------
  3069. // Robots will become aware of the player based on something that occurred.
  3070. // The object (probably player or weapon) which created the awareness is objp.
  3071. void create_awareness_event(object *objp, int type)
  3072. {
  3073. if (add_awareness_event(objp, type)) {
  3074. if (((rand() * (type+4)) >> 15) > 4)
  3075. Overall_agitation++;
  3076. if (Overall_agitation > OVERALL_AGITATION_MAX)
  3077. Overall_agitation = OVERALL_AGITATION_MAX;
  3078. }
  3079. }
  3080. byte New_awareness[MAX_SEGMENTS];
  3081. // ----------------------------------------------------------------------------------
  3082. void pae_aux(int segnum, int type, int level)
  3083. {
  3084. int j;
  3085. if (New_awareness[segnum] < type)
  3086. New_awareness[segnum] = type;
  3087. // Process children.
  3088. if (level <= 4)
  3089. for (j=0; j<MAX_SIDES_PER_SEGMENT; j++)
  3090. if (IS_CHILD(Segments[segnum].children[j]))
  3091. if (type == 4)
  3092. pae_aux(Segments[segnum].children[j], type-1, level+1);
  3093. else
  3094. pae_aux(Segments[segnum].children[j], type, level+1);
  3095. }
  3096. // ----------------------------------------------------------------------------------
  3097. void process_awareness_events(void)
  3098. {
  3099. int i;
  3100. for (i=0; i<=Highest_segment_index; i++)
  3101. New_awareness[i] = 0;
  3102. for (i=0; i<Num_awareness_events; i++)
  3103. pae_aux(Awareness_events[i].segnum, Awareness_events[i].type, 1);
  3104. Num_awareness_events = 0;
  3105. }
  3106. // ----------------------------------------------------------------------------------
  3107. void set_player_awareness_all(void)
  3108. {
  3109. int i;
  3110. process_awareness_events();
  3111. for (i=0; i<=Highest_object_index; i++)
  3112. if (Objects[i].control_type == CT_AI)
  3113. if (New_awareness[Objects[i].segnum] > Ai_local_info[i].player_awareness_type) {
  3114. Ai_local_info[i].player_awareness_type = New_awareness[Objects[i].segnum];
  3115. Ai_local_info[i].player_awareness_time = PLAYER_AWARENESS_INITIAL_TIME;
  3116. }
  3117. }
  3118. #ifndef NDEBUG
  3119. int Ai_dump_enable = 0;
  3120. FILE *Ai_dump_file = NULL;
  3121. char Ai_error_message[128] = "";
  3122. // ----------------------------------------------------------------------------------
  3123. void dump_ai_objects_all()
  3124. {
  3125. #if PARALLAX
  3126. int objnum;
  3127. int total=0;
  3128. time_t time_of_day;
  3129. time_of_day = time(NULL);
  3130. if (!Ai_dump_enable)
  3131. return;
  3132. if (Ai_dump_file == NULL)
  3133. Ai_dump_file = fopen("ai.out","a+t");
  3134. fprintf(Ai_dump_file, "\nnum: seg distance __mode__ behav. [velx vely velz] (Frame = %i)\n", FrameCount);
  3135. fprintf(Ai_dump_file, "Date & Time = %s\n", ctime(&time_of_day));
  3136. if (Ai_error_message[0])
  3137. fprintf(Ai_dump_file, "Error message: %s\n", Ai_error_message);
  3138. for (objnum=0; objnum <= Highest_object_index; objnum++) {
  3139. object *objp = &Objects[objnum];
  3140. ai_static *aip = &objp->ctype.ai_info;
  3141. ai_local *ailp = &Ai_local_info[objnum];
  3142. fix dist_to_player;
  3143. dist_to_player = vm_vec_dist(&objp->pos, &ConsoleObject->pos);
  3144. if (objp->control_type == CT_AI) {
  3145. fprintf(Ai_dump_file, "%3i: %3i %8.3f %8s %8s [%3i %4i]\n",
  3146. objnum, objp->segnum, f2fl(dist_to_player), mode_text[ailp->mode], behavior_text[aip->behavior-0x80], aip->hide_index, aip->path_length);
  3147. if (aip->path_length)
  3148. total += aip->path_length;
  3149. }
  3150. }
  3151. fprintf(Ai_dump_file, "Total path length = %4i\n", total);
  3152. #endif
  3153. }
  3154. // ----------------------------------------------------------------------------------
  3155. void force_dump_ai_objects_all(char *msg)
  3156. {
  3157. int tsave;
  3158. tsave = Ai_dump_enable;
  3159. Ai_dump_enable = 1;
  3160. sprintf(Ai_error_message, "%s\n", msg);
  3161. dump_ai_objects_all();
  3162. Ai_error_message[0] = 0;
  3163. Ai_dump_enable = tsave;
  3164. }
  3165. // ----------------------------------------------------------------------------------
  3166. void turn_off_ai_dump(void)
  3167. {
  3168. if (Ai_dump_file != NULL)
  3169. fclose(Ai_dump_file);
  3170. Ai_dump_file = NULL;
  3171. }
  3172. #endif
  3173. // ----------------------------------------------------------------------------------
  3174. // Do things which need to get done for all AI objects each frame.
  3175. // This includes:
  3176. // Setting player_awareness (a fix, time in seconds which object is aware of player)
  3177. void do_ai_frame_all(void)
  3178. {
  3179. #ifndef NDEBUG
  3180. dump_ai_objects_all();
  3181. #endif
  3182. set_player_awareness_all();
  3183. // if ((FrameCount & 0x07) == 0)
  3184. // mprintf((0, "[%i] ", Overall_agitation));
  3185. }
  3186. //--unused-- // ----------------------------------------------------------------------------------
  3187. //--unused-- // Reset various state information for a new life.
  3188. //--unused-- // Probably not going to be called for a new game because for that an entire object
  3189. //--unused-- // gets reloaded.
  3190. //--unused-- void reset_ai_states(object *objp)
  3191. //--unused-- {
  3192. //--unused-- int i;
  3193. //--unused-- //ai_static *aip = &objp->ctype.ai_info;
  3194. //--unused-- ai_local *ailp = &Ai_local_info[objp-Objects];
  3195. //--unused--
  3196. //--unused-- ailp->wait_time = 0;
  3197. //--unused-- ailp->next_fire = 0;
  3198. //--unused-- ailp->player_awareness_type = 0;
  3199. //--unused-- for (i=0; i<MAX_SUBMODELS; i++) {
  3200. //--unused-- ailp->goal_angles[i].p = 0; ailp->goal_angles[i].b = 0; ailp->goal_angles[i].h = 0;
  3201. //--unused-- ailp->delta_angles[i].p = 0; ailp->delta_angles[i].b = 0; ailp->delta_angles[i].h = 0;
  3202. //--unused-- ailp->goal_state[i] = 0;
  3203. //--unused-- ailp->achieved_state[i] = 0;
  3204. //--unused-- }
  3205. //--unused-- }
  3206. // Initializations to be performed for all robots for a new level.
  3207. void init_robots_for_level(void)
  3208. {
  3209. Overall_agitation = 0;
  3210. }
  3211. int ai_save_state( FILE * fp )
  3212. {
  3213. fwrite( &Ai_initialized, sizeof(int), 1, fp );
  3214. fwrite( &Overall_agitation, sizeof(int), 1, fp );
  3215. fwrite( Ai_local_info, sizeof(ai_local)*MAX_OBJECTS, 1, fp );
  3216. fwrite( Point_segs, sizeof(point_seg)*MAX_POINT_SEGS, 1, fp );
  3217. fwrite( Ai_cloak_info, sizeof(ai_cloak_info)*MAX_AI_CLOAK_INFO, 1, fp );
  3218. fwrite( &Boss_cloak_start_time, sizeof(fix), 1, fp );
  3219. fwrite( &Boss_cloak_end_time , sizeof(fix), 1, fp );
  3220. fwrite( &Last_teleport_time , sizeof(fix), 1, fp );
  3221. fwrite( &Boss_teleport_interval, sizeof(fix), 1, fp );
  3222. fwrite( &Boss_cloak_interval, sizeof(fix), 1, fp );
  3223. fwrite( &Boss_cloak_duration, sizeof(fix), 1, fp );
  3224. fwrite( &Last_gate_time, sizeof(fix), 1, fp );
  3225. fwrite( &Gate_interval, sizeof(fix), 1, fp );
  3226. fwrite( &Boss_dying_start_time, sizeof(fix), 1, fp );
  3227. fwrite( &Boss_dying, sizeof(int), 1, fp );
  3228. fwrite( &Boss_dying_sound_playing, sizeof(int), 1, fp );
  3229. fwrite( &Boss_hit_this_frame, sizeof(int), 1, fp );
  3230. fwrite( &Boss_been_hit, sizeof(int), 1, fp );
  3231. return 1;
  3232. }
  3233. int ai_restore_state( FILE * fp )
  3234. {
  3235. fread( &Ai_initialized, sizeof(int), 1, fp );
  3236. fread( &Overall_agitation, sizeof(int), 1, fp );
  3237. fread( Ai_local_info, sizeof(ai_local)*MAX_OBJECTS, 1, fp );
  3238. fread( Point_segs, sizeof(point_seg)*MAX_POINT_SEGS, 1, fp );
  3239. fread( Ai_cloak_info, sizeof(ai_cloak_info)*MAX_AI_CLOAK_INFO, 1, fp );
  3240. fread( &Boss_cloak_start_time, sizeof(fix), 1, fp );
  3241. fread( &Boss_cloak_end_time , sizeof(fix), 1, fp );
  3242. fread( &Last_teleport_time , sizeof(fix), 1, fp );
  3243. fread( &Boss_teleport_interval, sizeof(fix), 1, fp );
  3244. fread( &Boss_cloak_interval, sizeof(fix), 1, fp );
  3245. fread( &Boss_cloak_duration, sizeof(fix), 1, fp );
  3246. fread( &Last_gate_time, sizeof(fix), 1, fp );
  3247. fread( &Gate_interval, sizeof(fix), 1, fp );
  3248. fread( &Boss_dying_start_time, sizeof(fix), 1, fp );
  3249. fread( &Boss_dying, sizeof(int), 1, fp );
  3250. fread( &Boss_dying_sound_playing, sizeof(int), 1, fp );
  3251. fread( &Boss_hit_this_frame, sizeof(int), 1, fp );
  3252. fread( &Boss_been_hit, sizeof(int), 1, fp );
  3253. return 1;
  3254. }
  3255. // -- void show_path_and_other(object *objp )
  3256. // -- {
  3257. // -- int i;
  3258. // -- ai_static *aip = &objp->ctype.ai_info;
  3259. // -- for (i=0; i<aip->path_length; i++)
  3260. // -- mprintf((0, "%2i ", Point_segs[aip->hide_index+i].segnum));
  3261. // -- mprintf((0, "[pl: %i cur: st: %i]\n", ConsoleObject->segnum, objp->segnum, aip->hide_segment));
  3262. // -- }
  3263.