1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829 |
- /*
- THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
- SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
- END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
- ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
- IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
- SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
- FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
- CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
- AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
- COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
- */
- /*
- * $Source: f:/miner/source/main/rcs/ai.c $
- * $Revision: 2.11 $
- * $Author: john $
- * $Date: 1995/07/09 11:15:48 $
- *
- * Autonomous Individual movement.
- *
- * $Log: ai.c $
- * Revision 2.11 1995/07/09 11:15:48 john
- * Put in Mike's code to fix bug where bosses don't gate in bots after
- * 32767 seconds of playing.
- *
- * Revision 2.10 1995/06/15 12:31:08 john
- * Fixed bug with cheats getting enabled when you type
- * the whole alphabet.
- *
- * Revision 2.9 1995/05/26 16:16:18 john
- * Split SATURN into define's for requiring cd, using cd, etc.
- * Also started adding all the Rockwell stuff.
- *
- * Revision 2.8 1995/04/06 15:12:27 john
- * Fixed bug with insane not working.
- *
- * Revision 2.7 1995/03/30 16:36:44 mike
- * text localization.
- *
- * Revision 2.6 1995/03/28 11:22:24 john
- * Added cheats to save file. Changed lunacy text.
- *
- * Revision 2.5 1995/03/27 16:45:07 john
- * Fixed some cheat bugs. Added astral cheat.
- *
- * Revision 2.4 1995/03/24 15:29:17 mike
- * add new cheats.
- *
- * Revision 2.3 1995/03/21 14:39:45 john
- * Ifdef'd out the NETWORK code.
- *
- * Revision 2.2 1995/03/14 18:24:39 john
- * Force Destination Saturn to use CD-ROM drive.
- *
- * Revision 2.1 1995/03/06 16:47:14 mike
- * destination saturn
- *
- * Revision 2.0 1995/02/27 11:30:01 john
- * New version 2.0, which has no anonymous unions, builds with
- * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
- *
- * Revision 1.295 1995/02/22 13:23:04 allender
- * remove anonymous unions from object structure
- *
- * Revision 1.294 1995/02/13 11:00:43 rob
- * Make brain guys high enough to get an open slot.
- *
- * Revision 1.293 1995/02/13 10:31:55 mike
- * Make brains understand they can't open locked doors.
- *
- * Revision 1.292 1995/02/13 10:18:01 rob
- * Reduced brain guy's level of awareness to keep him from hogging slots.
- *
- * Revision 1.291 1995/02/11 12:27:12 mike
- * fix path-to-exit cheat.
- *
- * Revision 1.290 1995/02/11 01:56:30 mike
- * robots don't fire cheat.
- *
- * Revision 1.289 1995/02/10 17:15:09 rob
- * Fixed some stuff with 64 awareness stuff.
- *
- * Revision 1.288 1995/02/10 16:31:32 mike
- * oops.
- *
- * Revision 1.287 1995/02/10 16:24:45 mike
- * fix the network follow path fix.
- *
- * Revision 1.286 1995/02/10 16:11:40 mike
- * in serial or modem games, follow path guys don't move if far away and
- * can't see player.
- *
- * Revision 1.285 1995/02/09 13:11:35 mike
- * comment out a bunch of mprintfs.
- * add toaster (drops prox bombs, runs away) to boss gate list.
- *
- * Revision 1.284 1995/02/08 22:44:53 rob
- * Lowerd anger level for follow path of any sort.
- *
- * Revision 1.283 1995/02/08 22:30:43 mike
- * lower awareness on station guys if they are returning home (multiplayer).
- *
- * Revision 1.282 1995/02/08 17:01:06 rob
- * Fixed problem with toasters dropping of proximity bombs.
- *
- * Revision 1.281 1995/02/08 11:49:35 rob
- * Reduce Green-guy attack awareness level so we don't let him attack us too.
- *
- * Revision 1.280 1995/02/08 11:37:52 mike
- * Check for failures in call to obj_create.
- *
- * Revision 1.279 1995/02/07 20:38:46 mike
- * fix toasters in multiplayer
- *
- *
- * Revision 1.278 1995/02/07 16:51:07 mike
- * fix sound time play bug.
- *
- * Revision 1.277 1995/02/06 22:33:04 mike
- * make robots follow path better in cooperative/roboarchy.
- *
- * Revision 1.276 1995/02/06 18:15:42 rob
- * Added forced sends for evasion movemnet.
- *
- * Revision 1.275 1995/02/06 16:41:22 rob
- * Change some positioning calls.
- *
- * Revision 1.274 1995/02/06 11:40:33 mike
- * replace some lint-related hacks with clean, proper code.
- *
- * Revision 1.273 1995/02/04 17:28:19 mike
- * make station guys return better.
- *
- * Revision 1.272 1995/02/03 17:40:55 mike
- * fix problem with robots falling asleep if you sit in game overnight, not in pause...bah.
- *
- * Revision 1.271 1995/02/02 21:11:25 rob
- * Tweaking stuff for multiplayer ai.
- *
- * Revision 1.270 1995/02/02 17:32:06 john
- * Added Hack for Assert that Mike put in after using Lint to find
- * uninitialized variables.
- *
- * Revision 1.269 1995/02/02 16:46:31 mike
- * fix boss gating.
- *
- * Revision 1.268 1995/02/02 16:27:29 mike
- * make boss not put out infinite robots.
- *
- * Revision 1.267 1995/02/01 21:10:02 mike
- * lint found bug! player_visibility not initialized!
- *
- * Revision 1.266 1995/02/01 20:51:27 john
- * Lintized
- *
- * Revision 1.265 1995/02/01 17:14:05 mike
- * fix robot sounds.
- *
- * Revision 1.264 1995/01/31 16:16:40 mike
- * Comment out "Darn you, John" Int3().
- *
- * Revision 1.263 1995/01/30 20:55:04 mike
- * fix nonsense in robot firing when a player is cloaked.
- *
- * Revision 1.262 1995/01/30 17:15:10 rob
- * Fixed problems with bigboss eclip messages.
- * Tweaked robot position sending for modem purposes.
- *
- * Revision 1.261 1995/01/30 15:30:31 rob
- * Prevent non-master players from gating in robots.
- *
- * Revision 1.260 1995/01/30 13:30:55 mike
- * new cases for firing at other players were bogus, could send position
- * without permission.
- *
- * Revision 1.259 1995/01/30 13:01:17 mike
- * Make robots fire at player other than one they are controlled by sometimes.
- *
- * Revision 1.258 1995/01/29 16:09:17 rob
- * Trying to get robots to shoot at non-controlling players.
- *
- * Revision 1.257 1995/01/29 13:47:05 mike
- * Make boss have more fireballs on death, have until end (though silent at end).
- * Fix bug which was preventing him from teleporting until hit, so he'd always
- * be in the same place when the player enters the room.
- *
- * Revision 1.256 1995/01/28 17:40:18 mike
- * make boss teleport & gate before you see him.
- *
- * Revision 1.255 1995/01/27 17:02:08 mike
- * move code around, was sending one frame (or worse!) old robot information.
- *
- * Revision 1.254 1995/01/26 17:02:43 mike
- * make fusion cannon have more chrome, make fusion, mega rock you!
- *
- * Revision 1.253 1995/01/26 15:11:17 rob
- * Shutup! I fixed it!
- *
- * Revision 1.252 1995/01/26 15:08:55 rob
- * Changed robot gating to accomodate multiplayer.
- *
- * Revision 1.251 1995/01/26 14:49:04 rob
- * Increase awareness level for firing to 94.
- *
- * Revision 1.250 1995/01/26 12:41:20 mike
- * fix bogus multiplayer code, would send permission without getting permission.
- *
- * Revision 1.249 1995/01/26 12:23:23 rob
- * Removed defines that were moved to ai.h
- *
- * Revision 1.248 1995/01/25 23:38:48 mike
- * modify list of robots gated in by super boss.
- *
- * Revision 1.247 1995/01/25 21:21:13 rob
- * Trying to let robots fire at a player even if they're not in control.
- *
- * Revision 1.246 1995/01/25 13:50:37 mike
- * Robots make angry sounds.
- *
- * Revision 1.245 1995/01/25 10:53:47 mike
- * better handling of robots which poke out of mine and try to recover.
- *
- * Revision 1.244 1995/01/24 22:03:02 mike
- * Tricky code to move a robot to a legal position if he is poking out of
- * the mine, even if it means moving him to another segment.
- *
- * Revision 1.243 1995/01/24 20:12:06 rob
- * Changed robot fire awareness level from 74 to 94.
- *
- * Revision 1.242 1995/01/24 13:22:32 mike
- * make robots accelerate faster, and Difficulty_level dependent.
- *
- * Revision 1.241 1995/01/24 12:09:39 mike
- * make robots animate in multiplayer.
- *
- * Revision 1.240 1995/01/21 21:21:10 mike
- * Make boss only gate robots into specified segments.
- *
- * Revision 1.239 1995/01/20 20:21:26 mike
- * prevent unnecessary boss cloaking.
- *
- */
- #pragma off (unreferenced)
- static char rcsid[] = "$Id: ai.c 2.11 1995/07/09 11:15:48 john Exp $";
- #pragma on (unreferenced)
- #include <stdio.h>
- #include <stdlib.h>
- #include "inferno.h"
- #include "game.h"
- #include "mono.h"
- #include "3d.h"
- #include "object.h"
- #include "render.h"
- #include "error.h"
- #include "ai.h"
- #include "laser.h"
- #include "fvi.h"
- #include "polyobj.h"
- #include "bm.h"
- #include "weapon.h"
- #include "physics.h"
- #include "collide.h"
- #include "fuelcen.h"
- #include "player.h"
- #include "wall.h"
- #include "vclip.h"
- #include "digi.h"
- #include "fireball.h"
- #include "morph.h"
- #include "effects.h"
- #include "timer.h"
- #include "sounds.h"
- #include "cntrlcen.h"
- #include "multibot.h"
- #include "multi.h"
- #include "network.h"
- #include "gameseq.h"
- #include "key.h"
- #include "powerup.h"
- #include "gauges.h"
- #include "text.h"
- #ifdef EDITOR
- #include "editor\editor.h"
- #endif
- #ifndef NDEBUG
- #include "string.h"
- #include <time.h>
- #endif
- #define JOHN_CHEATS_SIZE_1 6
- #define JOHN_CHEATS_SIZE_2 6
- #define JOHN_CHEATS_SIZE_3 6
- ubyte john_cheats_1[JOHN_CHEATS_SIZE_1] = { KEY_P ^ 0x00 ^ 0x34,
- KEY_O ^ 0x10 ^ 0x34,
- KEY_B ^ 0x20 ^ 0x34,
- KEY_O ^ 0x30 ^ 0x34,
- KEY_Y ^ 0x40 ^ 0x34,
- KEY_S ^ 0x50 ^ 0x34 };
- #define PARALLAX 0 // If !0, then special debugging info for Parallax eyes only enabled.
- #define MIN_D 0x100
- int Flinch_scale = 4;
- int john_cheats_index_1; // POBOYS detonate reactor
- int Attack_scale = 24;
- #define ANIM_RATE (F1_0/16)
- #define DELTA_ANG_SCALE 16
- byte Mike_to_matt_xlate[] = {AS_REST, AS_REST, AS_ALERT, AS_ALERT, AS_FLINCH, AS_FIRE, AS_RECOIL, AS_REST};
- int john_cheats_index_2; // PORGYS high speed weapon firing
- // int No_ai_flag=0;
- #define OVERALL_AGITATION_MAX 100
- #define MAX_AI_CLOAK_INFO 8 // Must be a power of 2!
- typedef struct {
- fix last_time;
- vms_vector last_position;
- } ai_cloak_info;
- #define BOSS_CLOAK_DURATION (F1_0*7)
- #define BOSS_DEATH_DURATION (F1_0*6)
- #define BOSS_DEATH_SOUND_DURATION 0x2ae14 // 2.68 seconds
- // Amount of time since the current robot was last processed for things such as movement.
- // It is not valid to use FrameTime because robots do not get moved every frame.
- //fix AI_proc_time;
- int Num_boss_teleport_segs;
- short Boss_teleport_segs[MAX_BOSS_TELEPORT_SEGS];
- #ifndef SHAREWARE
- int Num_boss_gate_segs;
- short Boss_gate_segs[MAX_BOSS_TELEPORT_SEGS];
- #endif
- int john_cheats_index_3; // LUNACY lunacy (insane behavior, rookie firing)
- // ---------- John: These variables must be saved as part of gamesave. ----------
- int Ai_initialized = 0;
- int Overall_agitation;
- ai_local Ai_local_info[MAX_OBJECTS];
- point_seg Point_segs[MAX_POINT_SEGS];
- point_seg *Point_segs_free_ptr = Point_segs;
- ai_cloak_info Ai_cloak_info[MAX_AI_CLOAK_INFO];
- fix Boss_cloak_start_time = 0;
- fix Boss_cloak_end_time = 0;
- fix Last_teleport_time = 0;
- fix Boss_teleport_interval = F1_0*8;
- fix Boss_cloak_interval = F1_0*10; // Time between cloaks
- fix Boss_cloak_duration = BOSS_CLOAK_DURATION;
- fix Last_gate_time = 0;
- fix Gate_interval = F1_0*6;
- fix Boss_dying_start_time;
- int Boss_dying, Boss_dying_sound_playing, Boss_hit_this_frame;
- int Boss_been_hit=0;
- // ---------- John: End of variables which must be saved as part of gamesave. ----------
- int john_cheats_index_4; // PLETCHnnn paint robots
- int ai_evaded=0;
- #ifndef SHAREWARE
- // 0 mech
- // 1 green claw
- // 2 spider
- // 3 josh
- // 4 violet
- // 5 cloak vulcan
- // 6 cloak mech
- // 7 brain
- // 8 onearm
- // 9 plasma
- // 10 toaster
- // 11 bird
- // 12 missile bird
- // 13 polyhedron
- // 14 baby spider
- // 15 mini boss
- // 16 super mech
- // 17 shareware boss
- // 18 cloak-green ; note, gating in this guy benefits player, cloak objects
- // 19 vulcan
- // 20 toad
- // 21 4-claw
- // 22 quad-laser
- // 23 super boss
- // 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};
- 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};
- #define MAX_GATE_INDEX ( sizeof(Super_boss_gate_list) / sizeof(Super_boss_gate_list[0]) )
- #endif
- int Ai_info_enabled=0;
- int Robot_firing_enabled = 1;
- extern int Ugly_robot_cheat, Ugly_robot_texture, Laser_rapid_fire;
- extern byte Enable_john_cheat_1, Enable_john_cheat_2, Enable_john_cheat_3, Enable_john_cheat_4;
- ubyte john_cheats_3[2*JOHN_CHEATS_SIZE_3+1] = { KEY_Y ^ 0x67,
- KEY_E ^ 0x66,
- KEY_C ^ 0x65,
- KEY_A ^ 0x64,
- KEY_N ^ 0x63,
- KEY_U ^ 0x62,
- KEY_L ^ 0x61 };
- #define MAX_AWARENESS_EVENTS 64
- typedef struct awareness_event {
- short segnum; // segment the event occurred in
- short type; // type of event, defines behavior
- vms_vector pos; // absolute 3 space location of event
- } awareness_event;
- // These globals are set by a call to find_vector_intersection, which is a slow routine,
- // so we don't want to call it again (for this object) unless we have to.
- vms_vector Hit_pos;
- int Hit_type, Hit_seg;
- fvi_info Hit_data;
- int Num_awareness_events = 0;
- awareness_event Awareness_events[MAX_AWARENESS_EVENTS];
- vms_vector Believed_player_pos;
- #define AIS_MAX 8
- #define AIE_MAX 4
- //--unused-- int Processed_this_frame, LastFrameCount;
- #ifndef NDEBUG
- // Index into this array with ailp->mode
- char mode_text[8][9] = {
- "STILL ",
- "WANDER ",
- "FOL_PATH",
- "CHASE_OB",
- "RUN_FROM",
- "HIDE ",
- "FOL_PAT2",
- "OPENDOR2"
- };
- // Index into this array with aip->behavior
- char behavior_text[6][9] = {
- "STILL ",
- "NORMAL ",
- "HIDE ",
- "RUN_FROM",
- "FOLPATH ",
- "STATION "
- };
- // Index into this array with aip->GOAL_STATE or aip->CURRENT_STATE
- char state_text[8][5] = {
- "NONE",
- "REST",
- "SRCH",
- "LOCK",
- "FLIN",
- "FIRE",
- "RECO",
- "ERR_",
- };
- int Ai_animation_test=0;
- #endif
- // Current state indicates where the robot current is, or has just done.
- // Transition table between states for an AI object.
- // First dimension is trigger event.
- // Second dimension is current state.
- // Third dimension is goal state.
- // Result is new goal state.
- // ERR_ means something impossible has happened.
- byte Ai_transition_table[AI_MAX_EVENT][AI_MAX_STATE][AI_MAX_STATE] = {
- {
- // Event = AIE_FIRE, a nearby object fired
- // none rest srch lock flin fire reco // CURRENT is rows, GOAL is columns
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO}, // none
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO}, // rest
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO}, // search
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO}, // lock
- { AIS_ERR_, AIS_REST, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FIRE, AIS_RECO}, // flinch
- { AIS_ERR_, AIS_FIRE, AIS_FIRE, AIS_FIRE, AIS_FLIN, AIS_FIRE, AIS_RECO}, // fire
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_FIRE} // recoil
- },
- // Event = AIE_HITT, a nearby object was hit (or a wall was hit)
- {
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FLIN},
- { AIS_ERR_, AIS_REST, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FIRE, AIS_RECO},
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_FIRE}
- },
- // Event = AIE_COLL, player collided with robot
- {
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_RECO},
- { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_LOCK, AIS_FLIN, AIS_FLIN},
- { AIS_ERR_, AIS_REST, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FIRE, AIS_RECO},
- { AIS_ERR_, AIS_LOCK, AIS_LOCK, AIS_LOCK, AIS_FLIN, AIS_FIRE, AIS_FIRE}
- },
- // Event = AIE_HURT, player hurt robot (by firing at and hitting it)
- // Note, this doesn't necessarily mean the robot JUST got hit, only that that is the most recent thing that happened.
- {
- { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
- { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
- { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
- { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
- { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
- { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN},
- { AIS_ERR_, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN, AIS_FLIN}
- }
- };
- ubyte john_cheats_2[2*JOHN_CHEATS_SIZE_2] = { KEY_P ^ 0x00 ^ 0x43, 0x66,
- KEY_O ^ 0x10 ^ 0x43, 0x11,
- KEY_R ^ 0x20 ^ 0x43, 0x8,
- KEY_G ^ 0x30 ^ 0x43, 0x2,
- KEY_Y ^ 0x40 ^ 0x43, 0x0,
- KEY_S ^ 0x50 ^ 0x43 };
- // ---------------------------------------------------------
- // On entry, N_robot_types had darn sure better be set.
- // Mallocs N_robot_types robot_info structs into global Robot_info.
- void init_ai_system(void)
- {
- #if 0
- int i;
- mprintf((0, "Trying to malloc %i bytes for Robot_info.\n", N_robot_types * sizeof(*Robot_info)));
- Robot_info = (robot_info *) malloc( N_robot_types * sizeof(*Robot_info) );
- mprintf((0, "Robot_info = %i\n", Robot_info));
- for (i=0; i<N_robot_types; i++) {
- Robot_info[i].field_of_view = F1_0/2;
- Robot_info[i].firing_wait = F1_0;
- Robot_info[i].turn_time = F1_0*2;
- Robot_info[i].fire_power = F1_0;
- Robot_info[i].shield = F1_0/2;
- Robot_info[i].max_speed = F1_0*10;
- Robot_info[i].always_0xabcd = 0xabcd;
- }
- #endif
- }
- void john_cheat_func_1(int key)
- {
- if (!Cheats_enabled)
- return;
- if (key == (john_cheats_1[john_cheats_index_1] ^ (john_cheats_index_1 << 4) ^ 0x34)) {
- john_cheats_index_1++;
- if (john_cheats_index_1 == JOHN_CHEATS_SIZE_1) {
- do_controlcen_destroyed_stuff(NULL);
- john_cheats_index_1 = 0;
- digi_play_sample( SOUND_CHEATER, F1_0);
- }
- } else
- john_cheats_index_1 = 0;
- }
- // ---------------------------------------------------------------------------------------------------------------------
- // Given a behavior, set initial mode.
- int ai_behavior_to_mode(int behavior)
- {
- switch (behavior) {
- case AIB_STILL: return AIM_STILL;
- case AIB_NORMAL: return AIM_CHASE_OBJECT;
- case AIB_HIDE: return AIM_HIDE;
- case AIB_RUN_FROM: return AIM_RUN_FROM_OBJECT;
- case AIB_FOLLOW_PATH: return AIM_FOLLOW_PATH;
- case AIB_STATION: return AIM_STILL;
- default: Int3(); // Contact Mike: Error, illegal behavior type
- }
- return AIM_STILL;
- }
- // ---------------------------------------------------------------------------------------------------------------------
- // Call every time the player starts a new ship.
- void ai_init_boss_for_ship(void)
- {
- Boss_been_hit = 0;
- }
- // ---------------------------------------------------------------------------------------------------------------------
- // initial_mode == -1 means leave mode unchanged.
- void init_ai_object(int objnum, int behavior, int hide_segment)
- {
- object *objp = &Objects[objnum];
- ai_static *aip = &objp->ctype.ai_info;
- ai_local *ailp = &Ai_local_info[objnum];
- #ifdef DEST_SAT
- if (!(Game_mode & GM_MULTI) && Robot_info[objp->id].boss_flag) {
- mprintf((0, "Current_level_num = %i, Last_level = %i\n", Current_level_num, Last_level));
- if (Current_level_num != Last_level) {
- mprintf((0, "Removing boss, object num = %i\n", objnum));
- objp->id = 0;
- objp->flags |= OF_SHOULD_BE_DEAD;
- }
- }
- #endif
- if (behavior == 0) {
- // mprintf((0, "Behavior of 0 for object #%i, bashing to AIB_NORMAL.\n", objnum));
- behavior = AIB_NORMAL;
- objp->ctype.ai_info.behavior = behavior;
- }
- // mprintf((0, "Initializing object #%i\n", objnum));
- // mode is now set from the Robot dialog, so this should get overwritten.
- ailp->mode = AIM_STILL;
- ailp->previous_visibility = 0;
- if (behavior != -1) {
- aip->behavior = behavior;
- ailp->mode = ai_behavior_to_mode(aip->behavior);
- } else if (!((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR))) {
- mprintf((0, "[obj %i -> normal] ", objnum));
- aip->behavior = AIB_NORMAL;
- }
- // This is astonishingly stupid! This routine gets called by matcens! KILL KILL KILL!!! Point_segs_free_ptr = Point_segs;
- vm_vec_zero(&objp->mtype.phys_info.velocity);
- // -- ailp->wait_time = F1_0*5;
- ailp->player_awareness_time = 0;
- ailp->player_awareness_type = 0;
- aip->GOAL_STATE = AIS_SRCH;
- aip->CURRENT_STATE = AIS_REST;
- ailp->time_player_seen = GameTime;
- ailp->next_misc_sound_time = GameTime;
- ailp->time_player_sound_attacked = GameTime;
- if ((behavior == AIB_HIDE) || (behavior == AIB_FOLLOW_PATH) || (behavior == AIB_STATION) || (behavior == AIB_RUN_FROM)) {
- aip->hide_segment = hide_segment;
- ailp->goal_segment = hide_segment;
- aip->hide_index = -1; // This means the path has not yet been created.
- aip->cur_path_index = 0;
- }
- aip->SKIP_AI_COUNT = 0;
- if (Robot_info[objp->id].cloak_type == RI_CLOAKED_ALWAYS)
- aip->CLOAKED = 1;
- else
- aip->CLOAKED = 0;
- objp->mtype.phys_info.flags |= (PF_BOUNCE | PF_TURNROLL);
-
- aip->REMOTE_OWNER = -1;
- }
- void john_cheat_func_2(int key)
- {
- if (!Cheats_enabled)
- return;
- if (key == (john_cheats_2[2*john_cheats_index_2] ^ (john_cheats_index_2 << 4) ^ 0x43)) {
- john_cheats_index_2++;
- if (john_cheats_index_2 == JOHN_CHEATS_SIZE_2) {
- Laser_rapid_fire = 0xBADA55;
- do_megawow_powerup(200);
- john_cheats_index_2 = 0;
- digi_play_sample( SOUND_CHEATER, F1_0);
- }
- } else
- john_cheats_index_2 = 0;
- }
- // ---------------------------------------------------------------------------------------------------------------------
- void init_ai_objects(void)
- {
- int i;
- Point_segs_free_ptr = Point_segs;
- for (i=0; i<MAX_OBJECTS; i++) {
- object *objp = &Objects[i];
- if (objp->control_type == CT_AI)
- init_ai_object(i, objp->ctype.ai_info.behavior, objp->ctype.ai_info.hide_segment);
- }
- init_boss_segments(Boss_teleport_segs, &Num_boss_teleport_segs, 1);
- #ifndef SHAREWARE
- init_boss_segments(Boss_gate_segs, &Num_boss_gate_segs, 0);
- #endif
- Boss_dying_sound_playing = 0;
- Boss_dying = 0;
- Boss_been_hit = 0;
- #ifndef SHAREWARE
- Gate_interval = F1_0*5 - Difficulty_level*F1_0/2;
- #endif
- Ai_initialized = 1;
- }
- int Lunacy = 0;
- int Diff_save = 1;
- fix Firing_wait_copy[MAX_ROBOT_TYPES];
- byte Rapidfire_count_copy[MAX_ROBOT_TYPES];
- void do_lunacy_on(void)
- {
- int i;
- if ( !Lunacy ) {
- Lunacy = 1;
- Diff_save = Difficulty_level;
- Difficulty_level = NDL-1;
- for (i=0; i<MAX_ROBOT_TYPES; i++) {
- Firing_wait_copy[i] = Robot_info[i].firing_wait[NDL-1];
- Rapidfire_count_copy[i] = Robot_info[i].rapidfire_count[NDL-1];
-
- Robot_info[i].firing_wait[NDL-1] = Robot_info[i].firing_wait[1];
- Robot_info[i].rapidfire_count[NDL-1] = Robot_info[i].rapidfire_count[1];
- }
- }
- }
- void do_lunacy_off(void)
- {
- int i;
- if ( Lunacy ) {
- Lunacy = 0;
- for (i=0; i<MAX_ROBOT_TYPES; i++) {
- Robot_info[i].firing_wait[NDL-1] = Firing_wait_copy[i];
- Robot_info[i].rapidfire_count[NDL-1] = Rapidfire_count_copy[i];
- }
- Difficulty_level = Diff_save;
- }
- }
- void john_cheat_func_3(int key)
- {
- if (!Cheats_enabled)
- return;
- if (key == (john_cheats_3[JOHN_CHEATS_SIZE_3 - john_cheats_index_3] ^ (0x61 + john_cheats_index_3))) {
- if (john_cheats_index_3 == 4)
- john_cheats_index_3++;
- john_cheats_index_3++;
- if (john_cheats_index_3 == JOHN_CHEATS_SIZE_3+1) {
- if (Lunacy) {
- do_lunacy_off();
- HUD_init_message( TXT_NO_LUNACY );
- } else {
- do_lunacy_on();
- HUD_init_message( TXT_LUNACY );
- digi_play_sample( SOUND_CHEATER, F1_0);
- }
- john_cheats_index_3 = 0;
- }
- } else
- john_cheats_index_3 = 0;
- }
- // ----------------------------------------------------------------
- // Do *dest = *delta unless:
- // *delta is pretty small
- // and they are of different signs.
- void set_rotvel_and_saturate(fix *dest, fix delta)
- {
- if ((delta ^ *dest) < 0) {
- if (abs(delta) < F1_0/8) {
- // mprintf((0, "D"));
- *dest = delta/4;
- } else
- // mprintf((0, "d"));
- *dest = delta;
- } else {
- // mprintf((0, "!"));
- *dest = delta;
- }
- }
- //--debug-- #ifndef NDEBUG
- //--debug-- int Total_turns=0;
- //--debug-- int Prevented_turns=0;
- //--debug-- #endif
- #define AI_TURN_SCALE 1
- #define BABY_SPIDER_ID 14
- extern void physics_turn_towards_vector(vms_vector *goal_vector, object *obj, fix rate);
- //-------------------------------------------------------------------------------------------
- void ai_turn_towards_vector(vms_vector *goal_vector, object *objp, fix rate)
- {
- vms_vector new_fvec;
- fix dot;
- if ((objp->id == BABY_SPIDER_ID) && (objp->type == OBJ_ROBOT)) {
- physics_turn_towards_vector(goal_vector, objp, rate);
- return;
- }
- new_fvec = *goal_vector;
- dot = vm_vec_dot(goal_vector, &objp->orient.fvec);
- if (dot < (F1_0 - FrameTime/2)) {
- fix mag;
- fix new_scale = fixdiv(FrameTime * AI_TURN_SCALE, rate);
- vm_vec_scale(&new_fvec, new_scale);
- vm_vec_add2(&new_fvec, &objp->orient.fvec);
- mag = vm_vec_normalize_quick(&new_fvec);
- if (mag < F1_0/256) {
- mprintf((1, "Degenerate vector in ai_turn_towards_vector (mag = %7.3f)\n", f2fl(mag)));
- new_fvec = *goal_vector; // if degenerate vector, go right to goal
- }
- }
- // // Every 8th time, do a correct matrix create, 7/8 time, do a quick one.
- // if (rand() < 0x1000)
- vm_vector_2_matrix(&objp->orient, &new_fvec, NULL, &objp->orient.rvec);
- // else
- // vm_vector_2_matrix_norm(&objp->orient, &new_fvec, NULL, &objp->orient.rvec);
- //--{
- //--vms_vector tvec;
- //--fix mag;
- //--tvec = objp->orient.fvec;
- //--mag = vm_vec_mag(&tvec);
- //--mprintf((0, "mags = %7.3f ", f2fl(mag)));
- //--
- //--tvec = objp->orient.uvec;
- //--mag = vm_vec_mag(&tvec);
- //--mprintf((0, "%7.3f ", f2fl(mag)));
- //--
- //--tvec = objp->orient.rvec;
- //--mag = vm_vec_mag(&tvec);
- //--mprintf((0, "%7.3f\n", f2fl(mag)));
- //--}
- //--simpler, but buggy: // The cross product of the forward vector with the right vector is the up vector
- //--simpler, but buggy: vm_vec_cross(&new_uvec, &new_fvec, &objp->orient.rvec);
- //--simpler, but buggy: vm_vec_cross(&new_rvec, &new_uvec, &new_fvec);
- //--simpler, but buggy:
- //--simpler, but buggy: objp->orient.fvec = new_fvec;
- //--simpler, but buggy: objp->orient.rvec = new_rvec;
- //--simpler, but buggy: objp->orient.uvec = new_uvec;
- }
- // --------------------------------------------------------------------------------------------------------------------
- void ai_turn_randomly(vms_vector *vec_to_player, object *obj, fix rate, int previous_visibility)
- {
- vms_vector curvec;
- // Random turning looks too stupid, so 1/4 of time, cheat.
- if (previous_visibility)
- if (rand() > 0x7400) {
- ai_turn_towards_vector(vec_to_player, obj, rate);
- return;
- }
- //--debug-- if (rand() > 0x6000)
- //--debug-- Prevented_turns++;
- curvec = obj->mtype.phys_info.rotvel;
- curvec.y += F1_0/64;
- curvec.x += curvec.y/6;
- curvec.y += curvec.z/4;
- curvec.z += curvec.x/10;
- if (abs(curvec.x) > F1_0/8) curvec.x /= 4;
- if (abs(curvec.y) > F1_0/8) curvec.y /= 4;
- if (abs(curvec.z) > F1_0/8) curvec.z /= 4;
- obj->mtype.phys_info.rotvel = curvec;
- }
- // Overall_agitation affects:
- // Widens field of view. Field of view is in range 0..1 (specified in bitmaps.tbl as N/360 degrees).
- // Overall_agitation/128 subtracted from field of view, making robots see wider.
- // Increases distance to which robot will search to create path to player by Overall_agitation/8 segments.
- // Decreases wait between fire times by Overall_agitation/64 seconds.
- void john_cheat_func_4(int key)
- {
- if (!Cheats_enabled)
- return;
- switch (john_cheats_index_4) {
- case 3:
- if (key == KEY_T)
- john_cheats_index_4++;
- else
- john_cheats_index_4 = 0;
- break;
- case 1:
- if (key == KEY_L)
- john_cheats_index_4++;
- else
- john_cheats_index_4 = 0;
- break;
-
- case 2:
- if (key == KEY_E)
- john_cheats_index_4++;
- else
- john_cheats_index_4 = 0;
- break;
-
- case 0:
- if (key == KEY_P)
- john_cheats_index_4++;
- break;
- case 4:
- if (key == KEY_C)
- john_cheats_index_4++;
- else
- john_cheats_index_4 = 0;
- break;
-
- case 5:
- if (key == KEY_H)
- john_cheats_index_4++;
- else
- john_cheats_index_4 = 0;
- break;
-
- case 6:
- Ugly_robot_texture = 0;
- case 7:
- case 8:
- if ((key >= KEY_1) && (key <= KEY_0)) {
- john_cheats_index_4++;
- Ugly_robot_texture *= 10;
- if (key != KEY_0)
- Ugly_robot_texture += key - 1;
- if (john_cheats_index_4 == 9) {
- if (Ugly_robot_texture == 999) {
- Ugly_robot_cheat = 0;
- HUD_init_message( TXT_ROBOT_PAINTING_OFF );
- } else {
- HUD_init_message( TXT_ROBOT_PAINTING_ON, Ugly_robot_texture );
- Ugly_robot_cheat = 0xBADA55;
- }
- mprintf((0, "Paint value = %i\n", Ugly_robot_texture));
- john_cheats_index_4 = 0;
- }
- } else
- john_cheats_index_4 = 0;
-
- break;
- default:
- john_cheats_index_4 = 0;
- }
- }
- // --------------------------------------------------------------------------------------------------------------------
- // Returns:
- // 0 Player is not visible from object, obstruction or something.
- // 1 Player is visible, but not in field of view.
- // 2 Player is visible and in field of view.
- // Note: Uses Believed_player_pos as player's position for cloak effect.
- // NOTE: Will destructively modify *pos if *pos is outside the mine.
- int player_is_visible_from_object(object *objp, vms_vector *pos, fix field_of_view, vms_vector *vec_to_player)
- {
- fix dot;
- fvi_query fq;
- fq.p0 = pos;
- if ((pos->x != objp->pos.x) || (pos->y != objp->pos.y) || (pos->z != objp->pos.z)) {
- int segnum = find_point_seg(pos, objp->segnum);
- if (segnum == -1) {
- fq.startseg = objp->segnum;
- *pos = objp->pos;
- mprintf((1, "Object %i, gun is outside mine, moving towards center.\n", objp-Objects));
- move_towards_segment_center(objp);
- } else
- fq.startseg = segnum;
- } else
- fq.startseg = objp->segnum;
- fq.p1 = &Believed_player_pos;
- fq.rad = F1_0/4;
- fq.thisobjnum = objp-Objects;
- fq.ignore_obj_list = NULL;
- fq.flags = FQ_TRANSWALL | FQ_CHECK_OBJS; //what about trans walls???
- Hit_type = find_vector_intersection(&fq,&Hit_data);
- Hit_pos = Hit_data.hit_pnt;
- Hit_seg = Hit_data.hit_seg;
- if ((Hit_type == HIT_NONE) || ((Hit_type == HIT_OBJECT) && (Hit_data.hit_object == Players[Player_num].objnum))) {
- dot = vm_vec_dot(vec_to_player, &objp->orient.fvec);
- // 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)));
- if (dot > field_of_view - (Overall_agitation << 9)) {
- // mprintf((0, "I can see you!\n"));
- return 2;
- } else {
- // mprintf((0, "Damn, I could see you if I were looking...\n"));
- return 1;
- }
- } else {
- // mprintf((0, " ** Where are you? **\n"));
- return 0;
- }
- }
- // ------------------------------------------------------------------------------------------------------------------
- // Return 1 if animates, else return 0
- int do_silly_animation(object *objp)
- {
- int objnum = objp-Objects;
- jointpos *jp_list;
- int robot_type, gun_num, robot_state, num_joint_positions;
- polyobj_info *pobj_info = &objp->rtype.pobj_info;
- ai_static *aip = &objp->ctype.ai_info;
- // ai_local *ailp = &Ai_local_info[objnum];
- int num_guns, at_goal;
- int attack_type;
- int flinch_attack_scale = 1;
- robot_type = objp->id;
- num_guns = Robot_info[robot_type].n_guns;
- attack_type = Robot_info[robot_type].attack_type;
- if (num_guns == 0) {
- // mprintf((0, "Object #%i of type #%i has 0 guns.\n", objp-Objects, robot_type));
- return 0;
- }
- // This is a hack. All positions should be based on goal_state, not GOAL_STATE.
- robot_state = Mike_to_matt_xlate[aip->GOAL_STATE];
- // previous_robot_state = Mike_to_matt_xlate[aip->CURRENT_STATE];
- if (attack_type) // && ((robot_state == AS_FIRE) || (robot_state == AS_RECOIL)))
- flinch_attack_scale = Attack_scale;
- else if ((robot_state == AS_FLINCH) || (robot_state == AS_RECOIL))
- flinch_attack_scale = Flinch_scale;
- at_goal = 1;
- for (gun_num=0; gun_num <= num_guns; gun_num++) {
- int joint;
- num_joint_positions = robot_get_anim_state(&jp_list, robot_type, gun_num, robot_state);
- for (joint=0; joint<num_joint_positions; joint++) {
- fix delta_angle, delta_2;
- int jointnum = jp_list[joint].jointnum;
- vms_angvec *jp = &jp_list[joint].angles;
- vms_angvec *pobjp = &pobj_info->anim_angles[jointnum];
- if (jointnum >= Polygon_models[objp->rtype.pobj_info.model_num].n_models) {
- Int3(); // Contact Mike: incompatible data, illegal jointnum, problem in pof file?
- continue;
- }
- if (jp->p != pobjp->p) {
- if (gun_num == 0)
- at_goal = 0;
- Ai_local_info[objnum].goal_angles[jointnum].p = jp->p;
- delta_angle = jp->p - pobjp->p;
- if (delta_angle >= F1_0/2)
- delta_2 = -ANIM_RATE;
- else if (delta_angle >= 0)
- delta_2 = ANIM_RATE;
- else if (delta_angle >= -F1_0/2)
- delta_2 = -ANIM_RATE;
- else
- delta_2 = ANIM_RATE;
- if (flinch_attack_scale != 1)
- delta_2 *= flinch_attack_scale;
- Ai_local_info[objnum].delta_angles[jointnum].p = delta_2/DELTA_ANG_SCALE; // complete revolutions per second
- }
- if (jp->b != pobjp->b) {
- if (gun_num == 0)
- at_goal = 0;
- Ai_local_info[objnum].goal_angles[jointnum].b = jp->b;
- delta_angle = jp->b - pobjp->b;
- if (delta_angle >= F1_0/2)
- delta_2 = -ANIM_RATE;
- else if (delta_angle >= 0)
- delta_2 = ANIM_RATE;
- else if (delta_angle >= -F1_0/2)
- delta_2 = -ANIM_RATE;
- else
- delta_2 = ANIM_RATE;
- if (flinch_attack_scale != 1)
- delta_2 *= flinch_attack_scale;
- Ai_local_info[objnum].delta_angles[jointnum].b = delta_2/DELTA_ANG_SCALE; // complete revolutions per second
- }
- if (jp->h != pobjp->h) {
- if (gun_num == 0)
- at_goal = 0;
- Ai_local_info[objnum].goal_angles[jointnum].h = jp->h;
- delta_angle = jp->h - pobjp->h;
- if (delta_angle >= F1_0/2)
- delta_2 = -ANIM_RATE;
- else if (delta_angle >= 0)
- delta_2 = ANIM_RATE;
- else if (delta_angle >= -F1_0/2)
- delta_2 = -ANIM_RATE;
- else
- delta_2 = ANIM_RATE;
- if (flinch_attack_scale != 1)
- delta_2 *= flinch_attack_scale;
- Ai_local_info[objnum].delta_angles[jointnum].h = delta_2/DELTA_ANG_SCALE; // complete revolutions per second
- }
- }
- if (at_goal) {
- //ai_static *aip = &objp->ctype.ai_info;
- ai_local *ailp = &Ai_local_info[objp-Objects];
- ailp->achieved_state[gun_num] = ailp->goal_state[gun_num];
- if (ailp->achieved_state[gun_num] == AIS_RECO)
- ailp->goal_state[gun_num] = AIS_FIRE;
- if (ailp->achieved_state[gun_num] == AIS_FLIN)
- ailp->goal_state[gun_num] = AIS_LOCK;
- }
- }
- if (at_goal == 1) //num_guns)
- aip->CURRENT_STATE = aip->GOAL_STATE;
- return 1;
- }
- // ------------------------------------------------------------------------------------------
- // Move all sub-objects in an object towards their goals.
- // Current orientation of object is at: pobj_info.anim_angles
- // Goal orientation of object is at: ai_info.goal_angles
- // Delta orientation of object is at: ai_info.delta_angles
- void ai_frame_animation(object *objp)
- {
- int objnum = objp-Objects;
- int joint;
- int num_joints;
- num_joints = Polygon_models[objp->rtype.pobj_info.model_num].n_models;
- for (joint=1; joint<num_joints; joint++) {
- fix delta_to_goal;
- fix scaled_delta_angle;
- vms_angvec *curangp = &objp->rtype.pobj_info.anim_angles[joint];
- vms_angvec *goalangp = &Ai_local_info[objnum].goal_angles[joint];
- vms_angvec *deltaangp = &Ai_local_info[objnum].delta_angles[joint];
- #ifndef NDEBUG
- if (Ai_animation_test) {
- 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));
- }
- #endif
- delta_to_goal = goalangp->p - curangp->p;
- if (delta_to_goal > 32767)
- delta_to_goal = delta_to_goal - 65536;
- else if (delta_to_goal < -32767)
- delta_to_goal = 65536 + delta_to_goal;
- if (delta_to_goal) {
- scaled_delta_angle = fixmul(deltaangp->p, FrameTime) * DELTA_ANG_SCALE;
- curangp->p += scaled_delta_angle;
- if (abs(delta_to_goal) < abs(scaled_delta_angle))
- curangp->p = goalangp->p;
- }
- delta_to_goal = goalangp->b - curangp->b;
- if (delta_to_goal > 32767)
- delta_to_goal = delta_to_goal - 65536;
- else if (delta_to_goal < -32767)
- delta_to_goal = 65536 + delta_to_goal;
- if (delta_to_goal) {
- scaled_delta_angle = fixmul(deltaangp->b, FrameTime) * DELTA_ANG_SCALE;
- curangp->b += scaled_delta_angle;
- if (abs(delta_to_goal) < abs(scaled_delta_angle))
- curangp->b = goalangp->b;
- }
- delta_to_goal = goalangp->h - curangp->h;
- if (delta_to_goal > 32767)
- delta_to_goal = delta_to_goal - 65536;
- else if (delta_to_goal < -32767)
- delta_to_goal = 65536 + delta_to_goal;
- if (delta_to_goal) {
- scaled_delta_angle = fixmul(deltaangp->h, FrameTime) * DELTA_ANG_SCALE;
- curangp->h += scaled_delta_angle;
- if (abs(delta_to_goal) < abs(scaled_delta_angle))
- curangp->h = goalangp->h;
- }
- }
- }
- // ----------------------------------------------------------------------------------
- void set_next_fire_time(ai_local *ailp, robot_info *robptr)
- {
- ailp->rapidfire_count++;
- if (ailp->rapidfire_count < robptr->rapidfire_count[Difficulty_level]) {
- ailp->next_fire = min(F1_0/8, robptr->firing_wait[Difficulty_level]/2);
- } else {
- ailp->rapidfire_count = 0;
- ailp->next_fire = robptr->firing_wait[Difficulty_level];
- }
- }
- // ----------------------------------------------------------------------------------
- // When some robots collide with the player, they attack.
- // If player is cloaked, then robot probably didn't actually collide, deal with that here.
- void do_ai_robot_hit_attack(object *robot, object *player, vms_vector *collision_point)
- {
- ai_local *ailp = &Ai_local_info[robot-Objects];
- robot_info *robptr = &Robot_info[robot->id];
- //#ifndef NDEBUG
- if (!Robot_firing_enabled)
- return;
- //#endif
- // If player is dead, stop firing.
- if (Objects[Players[Player_num].objnum].type == OBJ_GHOST)
- return;
- if (robptr->attack_type == 1) {
- if (ailp->next_fire <= 0) {
- if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
- if (vm_vec_dist_quick(&ConsoleObject->pos, &robot->pos) < robot->size + ConsoleObject->size + F1_0*2)
- collide_player_and_nasty_robot( player, robot, collision_point );
- robot->ctype.ai_info.GOAL_STATE = AIS_RECO;
- set_next_fire_time(ailp, robptr);
- }
- }
- }
- extern int Player_exploded;
- // --------------------------------------------------------------------------------------------------------------------
- // Note: Parameter vec_to_player is only passed now because guns which aren't on the forward vector from the
- // center of the robot will not fire right at the player. We need to aim the guns at the player. Barring that, we cheat.
- // When this routine is complete, the parameter vec_to_player should not be necessary.
- void ai_fire_laser_at_player(object *obj, vms_vector *fire_point)
- {
- int objnum = obj-Objects;
- ai_local *ailp = &Ai_local_info[objnum];
- robot_info *robptr = &Robot_info[obj->id];
- vms_vector fire_vec;
- vms_vector bpp_diff;
- if (!Robot_firing_enabled)
- return;
- #ifndef NDEBUG
- // We should never be coming here for the green guy, as he has no laser!
- if (robptr->attack_type == 1)
- Int3(); // Contact Mike: This is impossible.
- #endif
- if (obj->control_type == CT_MORPH)
- return;
- // If player is exploded, stop firing.
- if (Player_exploded)
- return;
- // If player is cloaked, maybe don't fire based on how long cloaked and randomness.
- if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
- fix cloak_time = Ai_cloak_info[objnum % MAX_AI_CLOAK_INFO].last_time;
- if (GameTime - cloak_time > CLOAK_TIME_MAX/4)
- if (rand() > fixdiv(GameTime - cloak_time, CLOAK_TIME_MAX)/2) {
- set_next_fire_time(ailp, robptr);
- return;
- }
- }
- //-- // Find segment containing laser fire position. If the robot is straddling a segment, the position from
- //-- // which it fires may be in a different segment, which is bad news for find_vector_intersection. So, cast
- //-- // a ray from the object center (whose segment we know) to the laser position. Then, in the call to Laser_create_new
- //-- // use the data returned from this call to find_vector_intersection.
- //-- // Note that while find_vector_intersection is pretty slow, it is not terribly slow if the destination point is
- //-- // in the same segment as the source point.
- //--
- //-- fq.p0 = &obj->pos;
- //-- fq.startseg = obj->segnum;
- //-- fq.p1 = fire_point;
- //-- fq.rad = 0;
- //-- fq.thisobjnum = obj-Objects;
- //-- fq.ignore_obj_list = NULL;
- //-- fq.flags = FQ_TRANSWALL | FQ_CHECK_OBJS; //what about trans walls???
- //--
- //-- fate = find_vector_intersection(&fq, &hit_data);
- //-- if (fate != HIT_NONE)
- //-- return;
- // Set position to fire at based on difficulty level.
- bpp_diff.x = Believed_player_pos.x + (rand()-16384) * (NDL-Difficulty_level-1) * 4;
- bpp_diff.y = Believed_player_pos.y + (rand()-16384) * (NDL-Difficulty_level-1) * 4;
- bpp_diff.z = Believed_player_pos.z + (rand()-16384) * (NDL-Difficulty_level-1) * 4;
- // Half the time fire at the player, half the time lead the player.
- if (rand() > 16384) {
- vm_vec_normalized_dir_quick(&fire_vec, &bpp_diff, fire_point);
- } else {
- vms_vector player_direction_vector;
- vm_vec_sub(&player_direction_vector, &bpp_diff, &bpp_diff);
- // If player is not moving, fire right at him!
- // Note: If the robot fires in the direction of its forward vector, this is bad because the weapon does not
- // come out from the center of the robot; it comes out from the side. So it is common for the weapon to miss
- // its target. Ideally, we want to point the guns at the player. For now, just fire right at the player.
- if ((abs(player_direction_vector.x < 0x10000)) && (abs(player_direction_vector.y < 0x10000)) && (abs(player_direction_vector.z < 0x10000))) {
- vm_vec_normalized_dir_quick(&fire_vec, &bpp_diff, fire_point);
- // Player is moving. Determine where the player will be at the end of the next frame if he doesn't change his
- // behavior. Fire at exactly that point. This isn't exactly what you want because it will probably take the laser
- // a different amount of time to get there, since it will probably be a different distance from the player.
- // So, that's why we write games, instead of guiding missiles...
- } else {
- vm_vec_sub(&fire_vec, &bpp_diff, fire_point);
- vm_vec_scale(&fire_vec,fixmul(Weapon_info[Robot_info[obj->id].weapon_type].speed[Difficulty_level], FrameTime));
- vm_vec_add2(&fire_vec, &player_direction_vector);
- vm_vec_normalize_quick(&fire_vec);
- }
- }
- //#ifndef NDEBUG
- // if (robptr->boss_flag)
- // mprintf((0, "Boss (%i) fires!\n", obj-Objects));
- //#endif
- Laser_create_new_easy( &fire_vec, fire_point, obj-Objects, robptr->weapon_type, 1);
- #ifndef SHAREWARE
- #ifdef NETWORK
- if (Game_mode & GM_MULTI)
- {
- ai_multi_send_robot_position(objnum, -1);
- multi_send_robot_fire(objnum, obj->ctype.ai_info.CURRENT_GUN, &fire_vec);
- }
- #endif
- #endif
- create_awareness_event(obj, PA_NEARBY_ROBOT_FIRED);
- set_next_fire_time(ailp, robptr);
- // If the boss fired, allow him to teleport very soon (right after firing, cool!), pending other factors.
- if (robptr->boss_flag)
- Last_teleport_time -= Boss_teleport_interval/2;
- }
- // --------------------------------------------------------------------------------------------------------------------
- // vec_goal must be normalized, or close to it.
- void move_towards_vector(object *objp, vms_vector *vec_goal)
- {
- physics_info *pptr = &objp->mtype.phys_info;
- fix speed, dot, max_speed;
- robot_info *robptr = &Robot_info[objp->id];
- vms_vector vel;
- // Trying to move towards player. If forward vector much different than velocity vector,
- // bash velocity vector twice as much towards player as usual.
- vel = pptr->velocity;
- vm_vec_normalize_quick(&vel);
- dot = vm_vec_dot(&vel, &objp->orient.fvec);
- if (dot < 3*F1_0/4) {
- // This funny code is supposed to slow down the robot and move his velocity towards his direction
- // more quickly than the general code
- //-! mprintf((0, "Th "));
- pptr->velocity.x = pptr->velocity.x/2 + fixmul(vec_goal->x, FrameTime*32);
- pptr->velocity.y = pptr->velocity.y/2 + fixmul(vec_goal->y, FrameTime*32);
- pptr->velocity.z = pptr->velocity.z/2 + fixmul(vec_goal->z, FrameTime*32);
- } else {
- //-! mprintf((0, "Tn "));
- pptr->velocity.x += fixmul(vec_goal->x, FrameTime*64) * (Difficulty_level+5)/4;
- pptr->velocity.y += fixmul(vec_goal->y, FrameTime*64) * (Difficulty_level+5)/4;
- pptr->velocity.z += fixmul(vec_goal->z, FrameTime*64) * (Difficulty_level+5)/4;
- }
- speed = vm_vec_mag_quick(&pptr->velocity);
- max_speed = robptr->max_speed[Difficulty_level];
- // Green guy attacks twice as fast as he moves away.
- if (robptr->attack_type == 1)
- max_speed *= 2;
- if (speed > max_speed) {
- pptr->velocity.x = (pptr->velocity.x*3)/4;
- pptr->velocity.y = (pptr->velocity.y*3)/4;
- pptr->velocity.z = (pptr->velocity.z*3)/4;
- }
- }
- // --------------------------------------------------------------------------------------------------------------------
- void move_towards_player(object *objp, vms_vector *vec_to_player)
- // vec_to_player must be normalized, or close to it.
- {
- move_towards_vector(objp, vec_to_player);
- }
- // --------------------------------------------------------------------------------------------------------------------
- // I am ashamed of this: fast_flag == -1 means normal slide about. fast_flag = 0 means no evasion.
- void move_around_player(object *objp, vms_vector *vec_to_player, int fast_flag)
- {
- physics_info *pptr = &objp->mtype.phys_info;
- fix speed;
- robot_info *robptr = &Robot_info[objp->id];
- int objnum = objp-Objects;
- int dir;
- int dir_change;
- fix ft;
- vms_vector evade_vector;
- int count=0;
- if (fast_flag == 0)
- return;
- dir_change = 48;
- ft = FrameTime;
- if (ft < F1_0/32) {
- dir_change *= 8;
- count += 3;
- } else
- while (ft < F1_0/4) {
- dir_change *= 2;
- ft *= 2;
- count++;
- }
- dir = (FrameCount + (count+1) * (objnum*8 + objnum*4 + objnum)) & dir_change;
- dir >>= (4+count);
- Assert((dir >= 0) && (dir <= 3));
- switch (dir) {
- case 0:
- evade_vector.x = fixmul(vec_to_player->z, FrameTime*32);
- evade_vector.y = fixmul(vec_to_player->y, FrameTime*32);
- evade_vector.z = fixmul(-vec_to_player->x, FrameTime*32);
- break;
- case 1:
- evade_vector.x = fixmul(-vec_to_player->z, FrameTime*32);
- evade_vector.y = fixmul(vec_to_player->y, FrameTime*32);
- evade_vector.z = fixmul(vec_to_player->x, FrameTime*32);
- break;
- case 2:
- evade_vector.x = fixmul(-vec_to_player->y, FrameTime*32);
- evade_vector.y = fixmul(vec_to_player->x, FrameTime*32);
- evade_vector.z = fixmul(vec_to_player->z, FrameTime*32);
- break;
- case 3:
- evade_vector.x = fixmul(vec_to_player->y, FrameTime*32);
- evade_vector.y = fixmul(-vec_to_player->x, FrameTime*32);
- evade_vector.z = fixmul(vec_to_player->z, FrameTime*32);
- break;
- }
- // Note: -1 means normal circling about the player. > 0 means fast evasion.
- if (fast_flag > 0) {
- fix dot;
- // Only take evasive action if looking at player.
- // Evasion speed is scaled by percentage of shields left so wounded robots evade less effectively.
- dot = vm_vec_dot(vec_to_player, &objp->orient.fvec);
- if ((dot > robptr->field_of_view[Difficulty_level]) && !(ConsoleObject->flags & PLAYER_FLAGS_CLOAKED)) {
- fix damage_scale;
- damage_scale = fixdiv(objp->shields, robptr->strength);
- if (damage_scale > F1_0)
- damage_scale = F1_0; // Just in case...
- else if (damage_scale < 0)
- damage_scale = 0; // Just in case...
- vm_vec_scale(&evade_vector, i2f(fast_flag) + damage_scale);
- }
- }
- pptr->velocity.x += evade_vector.x;
- pptr->velocity.y += evade_vector.y;
- pptr->velocity.z += evade_vector.z;
- speed = vm_vec_mag_quick(&pptr->velocity);
- if (speed > robptr->max_speed[Difficulty_level]) {
- pptr->velocity.x = (pptr->velocity.x*3)/4;
- pptr->velocity.y = (pptr->velocity.y*3)/4;
- pptr->velocity.z = (pptr->velocity.z*3)/4;
- }
- }
- // --------------------------------------------------------------------------------------------------------------------
- void move_away_from_player(object *objp, vms_vector *vec_to_player, int attack_type)
- {
- fix speed;
- physics_info *pptr = &objp->mtype.phys_info;
- robot_info *robptr = &Robot_info[objp->id];
- int objref;
- pptr->velocity.x -= fixmul(vec_to_player->x, FrameTime*16);
- pptr->velocity.y -= fixmul(vec_to_player->y, FrameTime*16);
- pptr->velocity.z -= fixmul(vec_to_player->z, FrameTime*16);
- if (attack_type) {
- // Get value in 0..3 to choose evasion direction.
- objref = ((objp-Objects) ^ ((FrameCount + 3*(objp-Objects)) >> 5)) & 3;
- switch (objref) {
- case 0: vm_vec_scale_add2(&pptr->velocity, &objp->orient.uvec, FrameTime << 5); break;
- case 1: vm_vec_scale_add2(&pptr->velocity, &objp->orient.uvec, -FrameTime << 5); break;
- case 2: vm_vec_scale_add2(&pptr->velocity, &objp->orient.rvec, FrameTime << 5); break;
- case 3: vm_vec_scale_add2(&pptr->velocity, &objp->orient.rvec, -FrameTime << 5); break;
- default: Int3(); // Impossible, bogus value on objref, must be in 0..3
- }
- }
- speed = vm_vec_mag_quick(&pptr->velocity);
- if (speed > robptr->max_speed[Difficulty_level]) {
- pptr->velocity.x = (pptr->velocity.x*3)/4;
- pptr->velocity.y = (pptr->velocity.y*3)/4;
- pptr->velocity.z = (pptr->velocity.z*3)/4;
- }
- //--old-- fix speed, dot;
- //--old-- physics_info *pptr = &objp->mtype.phys_info;
- //--old-- robot_info *robptr = &Robot_info[objp->id];
- //--old--
- //--old-- // Trying to move away from player. If forward vector much different than velocity vector,
- //--old-- // bash velocity vector twice as much away from player as usual.
- //--old-- dot = vm_vec_dot(&pptr->velocity, &objp->orient.fvec);
- //--old-- if (dot > -3*F1_0/4) {
- //--old-- // This funny code is supposed to slow down the robot and move his velocity towards his direction
- //--old-- // more quickly than the general code
- //--old-- pptr->velocity.x = pptr->velocity.x/2 - fixmul(vec_to_player->x, FrameTime*16);
- //--old-- pptr->velocity.y = pptr->velocity.y/2 - fixmul(vec_to_player->y, FrameTime*16);
- //--old-- pptr->velocity.z = pptr->velocity.z/2 - fixmul(vec_to_player->z, FrameTime*16);
- //--old-- } else {
- //--old-- pptr->velocity.x -= fixmul(vec_to_player->x, FrameTime*16);
- //--old-- pptr->velocity.y -= fixmul(vec_to_player->y, FrameTime*16);
- //--old-- pptr->velocity.z -= fixmul(vec_to_player->z, FrameTime*16);
- //--old-- }
- //--old--
- //--old-- speed = vm_vec_mag_quick(&pptr->velocity);
- //--old--
- //--old-- if (speed > robptr->max_speed[Difficulty_level]) {
- //--old-- pptr->velocity.x = (pptr->velocity.x*3)/4;
- //--old-- pptr->velocity.y = (pptr->velocity.y*3)/4;
- //--old-- pptr->velocity.z = (pptr->velocity.z*3)/4;
- //--old-- }
- }
- // --------------------------------------------------------------------------------------------------------------------
- // Move towards, away_from or around player.
- // Also deals with evasion.
- // If the flag evade_only is set, then only allowed to evade, not allowed to move otherwise (must have mode == AIM_STILL).
- 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)
- {
- object *dobjp;
- robot_info *robptr = &Robot_info[objp->id];
- // See if should take avoidance.
- // New way, green guys don't evade: if ((robptr->attack_type == 0) && (objp->ctype.ai_info.danger_laser_num != -1)) {
- if (objp->ctype.ai_info.danger_laser_num != -1) {
- dobjp = &Objects[objp->ctype.ai_info.danger_laser_num];
- if ((dobjp->type == OBJ_WEAPON) && (dobjp->signature == objp->ctype.ai_info.danger_laser_signature)) {
- fix dot, dist_to_laser, field_of_view;
- vms_vector vec_to_laser, laser_fvec;
- field_of_view = Robot_info[objp->id].field_of_view[Difficulty_level];
- vm_vec_sub(&vec_to_laser, &dobjp->pos, &objp->pos);
- dist_to_laser = vm_vec_normalize_quick(&vec_to_laser);
- dot = vm_vec_dot(&vec_to_laser, &objp->orient.fvec);
- if (dot > field_of_view) {
- fix laser_robot_dot;
- vms_vector laser_vec_to_robot;
- // The laser is seen by the robot, see if it might hit the robot.
- // Get the laser's direction. If it's a polyobj, it can be gotten cheaply from the orientation matrix.
- if (dobjp->render_type == RT_POLYOBJ)
- laser_fvec = dobjp->orient.fvec;
- else { // Not a polyobj, get velocity and normalize.
- laser_fvec = dobjp->mtype.phys_info.velocity; //dobjp->orient.fvec;
- vm_vec_normalize_quick(&laser_fvec);
- }
- vm_vec_sub(&laser_vec_to_robot, &objp->pos, &dobjp->pos);
- vm_vec_normalize_quick(&laser_vec_to_robot);
- laser_robot_dot = vm_vec_dot(&laser_fvec, &laser_vec_to_robot);
- if ((laser_robot_dot > F1_0*7/8) && (dist_to_laser < F1_0*80)) {
- int evade_speed;
- ai_evaded = 1;
- evade_speed = Robot_info[objp->id].evade_speed[Difficulty_level];
- move_around_player(objp, vec_to_player, evade_speed);
- }
- }
- return;
- }
- }
- // If only allowed to do evade code, then done.
- // Hmm, perhaps brilliant insight. If want claw-type guys to keep coming, don't return here after evasion.
- if ((!robptr->attack_type) && evade_only)
- return;
- // If we fall out of above, then no object to be avoided.
- objp->ctype.ai_info.danger_laser_num = -1;
- // Green guy selects move around/towards/away based on firing time, not distance.
- if (robptr->attack_type == 1) {
- if (((ailp->next_fire > robptr->firing_wait[Difficulty_level]/4) && (dist_to_player < F1_0*30)) || Player_is_dead) {
- // 1/4 of time, move around player, 3/4 of time, move away from player
- if (rand() < 8192) {
- move_around_player(objp, vec_to_player, -1);
- } else {
- move_away_from_player(objp, vec_to_player, 1);
- }
- } else {
- move_towards_player(objp, vec_to_player);
- }
- } else {
- if (dist_to_player < circle_distance)
- move_away_from_player(objp, vec_to_player, 0);
- else if (dist_to_player < circle_distance*2)
- move_around_player(objp, vec_to_player, -1);
- else
- move_towards_player(objp, vec_to_player);
- }
- }
- // --------------------------------------------------------------------------------------------------------------------
- // Compute a somewhat random, normalized vector.
- void make_random_vector(vms_vector *vec)
- {
- vec->x = (rand() - 16384) | 1; // make sure we don't create null vector
- vec->y = rand() - 16384;
- vec->z = rand() - 16384;
- vm_vec_normalize_quick(vec);
- }
- #ifndef NDEBUG
- void mprintf_animation_info(object *objp)
- {
- ai_static *aip = &objp->ctype.ai_info;
- ai_local *ailp = &Ai_local_info[objp-Objects];
- if (!Ai_info_enabled)
- return;
- mprintf((0, "Goal = "));
- switch (aip->GOAL_STATE) {
- case AIS_NONE: mprintf((0, "NONE ")); break;
- case AIS_REST: mprintf((0, "REST ")); break;
- case AIS_SRCH: mprintf((0, "SRCH ")); break;
- case AIS_LOCK: mprintf((0, "LOCK ")); break;
- case AIS_FLIN: mprintf((0, "FLIN ")); break;
- case AIS_FIRE: mprintf((0, "FIRE ")); break;
- case AIS_RECO: mprintf((0, "RECO ")); break;
- case AIS_ERR_: mprintf((0, "ERR_ ")); break;
- }
- mprintf((0, " Cur = "));
- switch (aip->CURRENT_STATE) {
- case AIS_NONE: mprintf((0, "NONE ")); break;
- case AIS_REST: mprintf((0, "REST ")); break;
- case AIS_SRCH: mprintf((0, "SRCH ")); break;
- case AIS_LOCK: mprintf((0, "LOCK ")); break;
- case AIS_FLIN: mprintf((0, "FLIN ")); break;
- case AIS_FIRE: mprintf((0, "FIRE ")); break;
- case AIS_RECO: mprintf((0, "RECO ")); break;
- case AIS_ERR_: mprintf((0, "ERR_ ")); break;
- }
- mprintf((0, " Aware = "));
- switch (ailp->player_awareness_type) {
- case AIE_FIRE: mprintf((0, "FIRE ")); break;
- case AIE_HITT: mprintf((0, "HITT ")); break;
- case AIE_COLL: mprintf((0, "COLL ")); break;
- case AIE_HURT: mprintf((0, "HURT ")); break;
- }
- mprintf((0, "Next fire = %6.3f, Time = %6.3f\n", f2fl(ailp->next_fire), f2fl(ailp->player_awareness_time)));
- }
- #endif
- // -------------------------------------------------------------------------------------------------------------------
- int Break_on_object = -1;
- void do_firing_stuff(object *obj, int player_visibility, vms_vector *vec_to_player)
- {
- //mprintf((0, "!"));
- if (player_visibility >= 1) {
- // Now, if in robot's field of view, lock onto player
- fix dot = vm_vec_dot(&obj->orient.fvec, vec_to_player);
- //mprintf((0, "dot = %8x ", dot));
- if ((dot >= 7*F1_0/8) || (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
- ai_static *aip = &obj->ctype.ai_info;
- ai_local *ailp = &Ai_local_info[obj-Objects];
- switch (aip->GOAL_STATE) {
- case AIS_NONE:
- case AIS_REST:
- case AIS_SRCH:
- case AIS_LOCK:
- aip->GOAL_STATE = AIS_FIRE;
- if (ailp->player_awareness_type <= PA_NEARBY_ROBOT_FIRED) {
- ailp->player_awareness_type = PA_NEARBY_ROBOT_FIRED;
- ailp->player_awareness_time = PLAYER_AWARENESS_INITIAL_TIME;
- }
- break;
- }
- } else if (dot >= F1_0/2) {
- ai_static *aip = &obj->ctype.ai_info;
- switch (aip->GOAL_STATE) {
- case AIS_NONE:
- case AIS_REST:
- case AIS_SRCH:
- aip->GOAL_STATE = AIS_LOCK;
- break;
- }
- }
- }
- }
- // --------------------------------------------------------------------------------------------------------------------
- // If a hiding robot gets bumped or hit, he decides to find another hiding place.
- void do_ai_robot_hit(object *objp, int type)
- {
- if (objp->control_type == CT_AI) {
- if ((type == PA_WEAPON_ROBOT_COLLISION) || (type == PA_PLAYER_COLLISION))
- switch (objp->ctype.ai_info.behavior) {
- case AIM_HIDE:
- objp->ctype.ai_info.SUBMODE = AISM_GOHIDE;
- break;
- case AIM_STILL:
- Ai_local_info[objp-Objects].mode = AIM_CHASE_OBJECT;
- break;
- }
- }
- }
- #ifndef NDEBUG
- int Do_ai_flag=1;
- int Cvv_test=0;
- int Cvv_last_time[MAX_OBJECTS];
- int Gun_point_hack=0;
- #endif
- #define CHASE_TIME_LENGTH (F1_0*8)
- #define DEFAULT_ROBOT_SOUND_VOLUME F1_0
- int Robot_sound_volume=DEFAULT_ROBOT_SOUND_VOLUME;
- // --------------------------------------------------------------------------------------------------------------------
- // Note: This function could be optimized. Surely player_is_visible_from_object would benefit from the
- // information of a normalized vec_to_player.
- // Return player visibility:
- // 0 not visible
- // 1 visible, but robot not looking at player (ie, on an unobstructed vector)
- // 2 visible and in robot's field of view
- // -1 player is cloaked
- // If the player is cloaked, set vec_to_player based on time player cloaked and last uncloaked position.
- // Updates ailp->previous_visibility if player is not cloaked, in which case the previous visibility is left unchanged
- // and is copied to player_visibility
- 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)
- {
- if (!*flag) {
- if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
- fix delta_time, dist;
- int cloak_index = (objp-Objects) % MAX_AI_CLOAK_INFO;
- delta_time = GameTime - Ai_cloak_info[cloak_index].last_time;
- if (delta_time > F1_0*2) {
- vms_vector randvec;
- Ai_cloak_info[cloak_index].last_time = GameTime;
- make_random_vector(&randvec);
- vm_vec_scale_add2(&Ai_cloak_info[cloak_index].last_position, &randvec, 8*delta_time );
- }
- dist = vm_vec_normalized_dir_quick(vec_to_player, &Ai_cloak_info[cloak_index].last_position, pos);
- *player_visibility = player_is_visible_from_object(objp, pos, robptr->field_of_view[Difficulty_level], vec_to_player);
- // *player_visibility = 2;
- if ((ailp->next_misc_sound_time < GameTime) && (ailp->next_fire < F1_0) && (dist < F1_0*20)) {
- mprintf((0, "ANGRY! "));
- ailp->next_misc_sound_time = GameTime + (rand() + F1_0) * (7 - Difficulty_level) / 1;
- digi_link_sound_to_pos( robptr->see_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
- }
- } else {
- // Compute expensive stuff -- vec_to_player and player_visibility
- vm_vec_normalized_dir_quick(vec_to_player, &Believed_player_pos, pos);
- if ((vec_to_player->x == 0) && (vec_to_player->y == 0) && (vec_to_player->z == 0)) {
- mprintf((0, "Warning: Player and robot at exactly the same location.\n"));
- vec_to_player->x = F1_0;
- }
- *player_visibility = player_is_visible_from_object(objp, pos, robptr->field_of_view[Difficulty_level], vec_to_player);
- // This horrible code added by MK in desperation on 12/13/94 to make robots wake up as soon as they
- // see you without killing frame rate.
- {
- ai_static *aip = &objp->ctype.ai_info;
- if ((*player_visibility == 2) && (ailp->previous_visibility != 2))
- if ((aip->GOAL_STATE == AIS_REST) || (aip->CURRENT_STATE == AIS_REST)) {
- aip->GOAL_STATE = AIS_FIRE;
- aip->CURRENT_STATE = AIS_FIRE;
- }
- }
- if (!Player_exploded && (ailp->previous_visibility != *player_visibility) && (*player_visibility == 2)) {
- if (ailp->previous_visibility == 0) {
- if (ailp->time_player_seen + F1_0/2 < GameTime) {
- // mprintf((0, "SEE! "));
- digi_link_sound_to_pos( robptr->see_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
- ailp->time_player_sound_attacked = GameTime;
- ailp->next_misc_sound_time = GameTime + F1_0 + rand()*4;
- }
- } else if (ailp->time_player_sound_attacked + F1_0/4 < GameTime) {
- // mprintf((0, "ANGRY! "));
- digi_link_sound_to_pos( robptr->attack_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
- ailp->time_player_sound_attacked = GameTime;
- }
- }
- if ((*player_visibility == 2) && (ailp->next_misc_sound_time < GameTime)) {
- // mprintf((0, "ATTACK! "));
- ailp->next_misc_sound_time = GameTime + (rand() + F1_0) * (7 - Difficulty_level) / 2;
- digi_link_sound_to_pos( robptr->attack_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
- }
- ailp->previous_visibility = *player_visibility;
- }
- *flag = 1;
- if (*player_visibility) {
- ailp->time_player_seen = GameTime;
- }
- }
- }
- // --------------------------------------------------------------------------------------------------------------------
- // Move the object objp to a spot in which it doesn't intersect a wall.
- // It might mean moving it outside its current segment.
- void move_object_to_legal_spot(object *objp)
- {
- vms_vector original_pos = objp->pos;
- int i;
- segment *segp = &Segments[objp->segnum];
- for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
- if (WALL_IS_DOORWAY(segp, i) & WID_FLY_FLAG) {
- vms_vector segment_center, goal_dir;
- fix dist_to_center;
- compute_segment_center(&segment_center, &Segments[segp->children[i]]);
- vm_vec_sub(&goal_dir, &segment_center, &objp->pos);
- dist_to_center = vm_vec_normalize_quick(&goal_dir);
- vm_vec_scale(&goal_dir, objp->size);
- vm_vec_add2(&objp->pos, &goal_dir);
- if (!object_intersects_wall(objp)) {
- int new_segnum = find_point_seg(&objp->pos, objp->segnum);
- if (new_segnum != -1) {
- obj_relink(objp-Objects, new_segnum);
- return;
- }
- } else
- objp->pos = original_pos;
- }
- }
- // Int3(); // Darn you John, you done it again! (But contact Mike)
- mprintf((0, "Note: Killing robot #%i because he's badly stuck outside the mine.\n", objp-Objects));
- apply_damage_to_robot(objp, objp->shields*2, objp-Objects);
- }
- // --------------------------------------------------------------------------------------------------------------------
- // Move object one object radii from current position towards segment center.
- // If segment center is nearer than 2 radii, move it to center.
- void move_towards_segment_center(object *objp)
- {
- int segnum = objp->segnum;
- fix dist_to_center;
- vms_vector segment_center, goal_dir;
- compute_segment_center(&segment_center, &Segments[segnum]);
- vm_vec_sub(&goal_dir, &segment_center, &objp->pos);
- dist_to_center = vm_vec_normalize_quick(&goal_dir);
- if (dist_to_center < objp->size) {
- // Center is nearer than the distance we want to move, so move to center.
- objp->pos = segment_center;
- 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)));
- if (object_intersects_wall(objp)) {
- mprintf((0, "Object #%i still illegal, trying trickier move.\n"));
- move_object_to_legal_spot(objp);
- }
- } else {
- int new_segnum;
- // Move one radii towards center.
- vm_vec_scale(&goal_dir, objp->size);
- vm_vec_add2(&objp->pos, &goal_dir);
- new_segnum = find_point_seg(&objp->pos, objp->segnum);
- if (new_segnum == -1) {
- objp->pos = segment_center;
- move_object_to_legal_spot(objp);
- }
- 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))));
- }
- }
- // -----------------------------------------------------------------------------------------------------------
- // Return true if door can be flown through by a suitable type robot.
- // Only brains and avoid robots can open doors.
- int ai_door_is_openable(object *objp, segment *segp, int sidenum)
- {
- int wall_num;
- // The mighty console object can open all doors (for purposes of determining paths).
- if (objp == ConsoleObject) {
- int wall_num = segp->sides[sidenum].wall_num;
- if (Walls[wall_num].type == WALL_DOOR)
- return 1;
- }
- if ((objp->id == ROBOT_BRAIN) || (objp->ctype.ai_info.behavior == AIB_RUN_FROM)) {
- wall_num = segp->sides[sidenum].wall_num;
- if (wall_num != -1)
- if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].keys == KEY_NONE) && !(Walls[wall_num].flags & WALL_DOOR_LOCKED))
- return 1;
- }
- return 0;
- }
- //--// -----------------------------------------------------------------------------------------------------------
- //--// Return true if object *objp is allowed to open door at wall_num
- //--int door_openable_by_robot(object *objp, int wall_num)
- //--{
- //-- if (objp->id == ROBOT_BRAIN)
- //-- if (Walls[wall_num].keys == KEY_NONE)
- //-- return 1;
- //--
- //-- return 0;
- //--}
- // -----------------------------------------------------------------------------------------------------------
- // Return side of openable door in segment, if any. If none, return -1.
- int openable_doors_in_segment(object *objp)
- {
- int i;
- int segnum = objp->segnum;
- for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
- if (Segments[segnum].sides[i].wall_num != -1) {
- int wall_num = Segments[segnum].sides[i].wall_num;
- 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))
- return i;
- }
- }
- return -1;
- }
- //--unused-- // -----------------------------------------------------------------------------------------------------------
- //--unused-- // For all doors this guy can open in his segment, open them.
- //--unused-- void ai_open_doors_in_segment(object *objp)
- //--unused-- {
- //--unused-- int i;
- //--unused-- int segnum = objp->segnum;
- //--unused--
- //--unused-- for (i=0; i<MAX_SIDES_PER_SEGMENT; i++)
- //--unused-- if (Segments[segnum].sides[i].wall_num != -1) {
- //--unused-- int wall_num = Segments[segnum].sides[i].wall_num;
- //--unused-- if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].keys == KEY_NONE) && (Walls[wall_num].state == WALL_DOOR_CLOSED))
- //--unused-- if (door_openable_by_robot(objp, wall_num)) {
- //--unused-- mprintf((0, "Trying to open door at segment %i, side %i\n", segnum, i));
- //--unused-- wall_open_door(&Segments[segnum], i);
- //--unused-- }
- //--unused-- }
- //--unused-- }
- // --------------------------------------------------------------------------------------------------------------------
- // Return true if a special object (player or control center) is in this segment.
- int special_object_in_seg(int segnum)
- {
- int objnum;
- objnum = Segments[segnum].objects;
- while (objnum != -1) {
- if ((Objects[objnum].type == OBJ_PLAYER) || (Objects[objnum].type == OBJ_CNTRLCEN)) {
- mprintf((0, "Special object of type %i in segment %i\n", Objects[objnum].type, segnum));
- return 1;
- } else
- objnum = Objects[objnum].next;
- }
- return 0;
- }
- // --------------------------------------------------------------------------------------------------------------------
- // Randomly select a segment attached to *segp, reachable by flying.
- int get_random_child(int segnum)
- {
- int sidenum;
- segment *segp = &Segments[segnum];
- sidenum = (rand() * 6) >> 15;
- while (!(WALL_IS_DOORWAY(segp, sidenum) & WID_FLY_FLAG))
- sidenum = (rand() * 6) >> 15;
- segnum = segp->children[sidenum];
- return segnum;
- }
- // --------------------------------------------------------------------------------------------------------------------
- // Return true if placing an object of size size at pos *pos intersects a (player or robot or control center) in segment *segp.
- int check_object_object_intersection(vms_vector *pos, fix size, segment *segp)
- {
- int curobjnum;
- // If this would intersect with another object (only check those in this segment), then try to move.
- curobjnum = segp->objects;
- while (curobjnum != -1) {
- object *curobjp = &Objects[curobjnum];
- if ((curobjp->type == OBJ_PLAYER) || (curobjp->type == OBJ_ROBOT) || (curobjp->type == OBJ_CNTRLCEN)) {
- if (vm_vec_dist_quick(pos, &curobjp->pos) < size + curobjp->size)
- return 1;
- }
- curobjnum = curobjp->next;
- }
- return 0;
- }
- #ifndef SHAREWARE
- // --------------------------------------------------------------------------------------------------------------------
- // Return true if object created, else return false.
- int create_gated_robot( int segnum, int object_id)
- {
- int objnum;
- object *objp;
- segment *segp = &Segments[segnum];
- vms_vector object_pos;
- robot_info *robptr = &Robot_info[object_id];
- int i, count=0;
- fix objsize = Polygon_models[robptr->model_num].rad;
- int default_behavior;
- for (i=0; i<=Highest_object_index; i++)
- if (Objects[i].type == OBJ_ROBOT)
- if (Objects[i].matcen_creator == BOSS_GATE_MATCEN_NUM)
- count++;
- if (count > 2*Difficulty_level + 3) {
- // mprintf((0, "Cannot gate in a robot until you kill one.\n"));
- Last_gate_time = GameTime - 3*Gate_interval/4;
- return 0;
- }
- compute_segment_center(&object_pos, segp);
- pick_random_point_in_seg(&object_pos, segp-Segments);
- // See if legal to place object here. If not, move about in segment and try again.
- if (check_object_object_intersection(&object_pos, objsize, segp)) {
- // mprintf((0, "Can't get in because object collides with something.\n"));
- Last_gate_time = GameTime - 3*Gate_interval/4;
- return 0;
- }
- objnum = obj_create(OBJ_ROBOT, object_id, segnum, &object_pos, &vmd_identity_matrix, objsize, CT_AI, MT_PHYSICS, RT_POLYOBJ);
- if ( objnum < 0 ) {
- // mprintf((1, "Can't get object to gate in robot. Not gating in.\n"));
- Last_gate_time = GameTime - 3*Gate_interval/4;
- return 0;
- }
- mprintf((0, "Gating in object %i in segment %i\n", objnum, segp-Segments));
- #ifdef NETWORK
- Net_create_objnums[0] = objnum; // A convenient global to get objnum back to caller for multiplayer
- #endif
- objp = &Objects[objnum];
- //Set polygon-object-specific data
- objp->rtype.pobj_info.model_num = robptr->model_num;
- objp->rtype.pobj_info.subobj_flags = 0;
- //set Physics info
- objp->mtype.phys_info.mass = robptr->mass;
- objp->mtype.phys_info.drag = robptr->drag;
- objp->mtype.phys_info.flags |= (PF_LEVELLING);
- objp->shields = robptr->strength;
- objp->matcen_creator = BOSS_GATE_MATCEN_NUM; // flag this robot as having been created by the boss.
- default_behavior = AIB_NORMAL;
- if (object_id == 10) // This is a toaster guy!
- default_behavior = AIB_RUN_FROM;
- init_ai_object(objp-Objects, default_behavior, -1 ); // Note, -1 = segment this robot goes to to hide, should probably be something useful
- object_create_explosion(segnum, &object_pos, i2f(10), VCLIP_MORPHING_ROBOT );
- digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, segnum, 0, &object_pos, 0 , F1_0);
- morph_start(objp);
- Last_gate_time = GameTime;
- Players[Player_num].num_robots_level++;
- Players[Player_num].num_robots_total++;
- return 1;
- }
- // --------------------------------------------------------------------------------------------------------------------
- // Make object objp gate in a robot.
- // The process of him bringing in a robot takes one second.
- // Then a robot appears somewhere near the player.
- // Return true if robot successfully created, else return false
- int gate_in_robot(int type, int segnum)
- {
- if (segnum < 0)
- segnum = Boss_gate_segs[(rand() * Num_boss_gate_segs) >> 15];
- Assert((segnum >= 0) && (segnum <= Highest_segment_index));
- return create_gated_robot(segnum, type);
- }
- #endif
- //// --------------------------------------------------------------------------------------------------------------------
- //// Return true if some distance function of vertices implies segment is too small for object.
- //int segment_too_small_for_object(object *objp, segment *segp)
- //{
- // int i;
- // fix threshold_distance;
- //
- // threshold_distance = objp->size*2;
- //
- // for (i=1; i<MAX_VERTICES_PER_SEGMENT; i++)
- // if (vm_vec_dist_quick(&Vertices[segp->verts[i]], &Vertices[segp->verts[i-1]]) < threshold_distance) {
- //#ifndef NDEBUG
- // fix dist = vm_vec_dist_quick(&Vertices[segp->verts[i]], &Vertices[segp->verts[i-1]]);
- //#endif
- // 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)));
- // return 1;
- // }
- //
- // return 0;
- //}
- //--unused-- int Shown_all_segments=0;
- // --------------------------------------------------------------------------------------------------------------------
- int boss_fits_in_seg(object *boss_objp, int segnum)
- {
- vms_vector segcenter;
- int boss_objnum = boss_objp-Objects;
- int posnum;
- compute_segment_center(&segcenter, &Segments[segnum]);
- for (posnum=0; posnum<9; posnum++) {
- if (posnum > 0) {
- vms_vector vertex_pos;
- Assert((posnum-1 >= 0) && (posnum-1 < 8));
- vertex_pos = Vertices[Segments[segnum].verts[posnum-1]];
- vm_vec_avg(&boss_objp->pos, &vertex_pos, &segcenter);
- } else
- boss_objp->pos = segcenter;
- obj_relink(boss_objnum, segnum);
- if (!object_intersects_wall(boss_objp))
- return 1;
- }
- return 0;
- }
- #define QUEUE_SIZE 256
- // --------------------------------------------------------------------------------------------------------------------
- // Create list of segments boss is allowed to teleport to at segptr.
- // Set *num_segs.
- // Boss is allowed to teleport to segments he fits in (calls object_intersects_wall) and
- // he can reach from his initial position (calls find_connected_distance).
- // If size_check is set, then only add segment if boss can fit in it, else any segment is legal.
- void init_boss_segments(short segptr[], int *num_segs, int size_check)
- {
- int boss_objnum=-1;
- int i;
- *num_segs = 0;
- #ifdef EDITOR
- N_selected_segs = 0;
- #endif
- // See if there is a boss. If not, quick out.
- for (i=0; i<=Highest_object_index; i++)
- if ((Objects[i].type == OBJ_ROBOT) && (Robot_info[Objects[i].id].boss_flag)) {
- Assert(boss_objnum == -1); // There are two bosses in this mine! i and boss_objnum!
- boss_objnum = i;
- }
- if (boss_objnum != -1) {
- int original_boss_seg;
- vms_vector original_boss_pos;
- object *boss_objp = &Objects[boss_objnum];
- int head, tail;
- int seg_queue[QUEUE_SIZE];
- //ALREADY IN RENDER.H byte visited[MAX_SEGMENTS];
- fix boss_size_save;
- boss_size_save = boss_objp->size;
- boss_objp->size = fixmul((F1_0/4)*3, boss_objp->size);
- original_boss_seg = boss_objp->segnum;
- original_boss_pos = boss_objp->pos;
- head = 0;
- tail = 0;
- seg_queue[head++] = original_boss_seg;
- segptr[(*num_segs)++] = original_boss_seg;
- #ifdef EDITOR
- Selected_segs[N_selected_segs++] = original_boss_seg;
- #endif
- for (i=0; i<=Highest_segment_index; i++)
- visited[i] = 0;
- while (tail != head) {
- int sidenum;
- segment *segp = &Segments[seg_queue[tail++]];
- tail &= QUEUE_SIZE-1;
- for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
- if (WALL_IS_DOORWAY(segp, sidenum) & WID_FLY_FLAG) {
- if (visited[segp->children[sidenum]] == 0) {
- seg_queue[head++] = segp->children[sidenum];
- visited[segp->children[sidenum]] = 1;
- head &= QUEUE_SIZE-1;
- if (head > tail) {
- if (head == tail + QUEUE_SIZE-1)
- Int3(); // queue overflow. Make it bigger!
- } else
- if (head+QUEUE_SIZE == tail + QUEUE_SIZE-1)
- Int3(); // queue overflow. Make it bigger!
-
- if ((!size_check) || boss_fits_in_seg(boss_objp, segp->children[sidenum])) {
- segptr[(*num_segs)++] = segp->children[sidenum];
- #ifdef EDITOR
- Selected_segs[N_selected_segs++] = segp->children[sidenum];
- #endif
- if (*num_segs >= MAX_BOSS_TELEPORT_SEGS) {
- 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));
- tail = head;
- }
- }
- }
- }
- }
- }
- boss_objp->size = boss_size_save;
- boss_objp->pos = original_boss_pos;
- obj_relink(boss_objnum, original_boss_seg);
- }
- }
- // --------------------------------------------------------------------------------------------------------------------
- void teleport_boss(object *objp)
- {
- int rand_segnum;
- vms_vector boss_dir;
- int rand_seg;
- Assert(Num_boss_teleport_segs > 0);
- // Pick a random segment from the list of boss-teleportable-to segments.
- rand_seg = (rand() * Num_boss_teleport_segs) >> 15;
- rand_segnum = Boss_teleport_segs[rand_seg];
- Assert((rand_segnum >= 0) && (rand_segnum <= Highest_segment_index));
- #ifndef SHAREWARE
- #ifdef NETWORK
- if (Game_mode & GM_MULTI)
- multi_send_boss_actions(objp-Objects, 1, rand_seg, 0);
- #endif
- #endif
- compute_segment_center(&objp->pos, &Segments[rand_segnum]);
- obj_relink(objp-Objects, rand_segnum);
- Last_teleport_time = GameTime;
- // make boss point right at player
- vm_vec_sub(&boss_dir, &Objects[Players[Player_num].objnum].pos, &objp->pos);
- vm_vector_2_matrix(&objp->orient, &boss_dir, NULL, NULL);
- digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, rand_segnum, 0, &objp->pos, 0 , F1_0);
- digi_kill_sound_linked_to_object( objp-Objects);
- 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
- #ifndef NDEBUG
- mprintf((0, "Boss teleported to segment %i\n", rand_segnum));
- #endif
- // After a teleport, boss can fire right away.
- Ai_local_info[objp-Objects].next_fire = 0;
- }
- // ----------------------------------------------------------------------
- void start_boss_death_sequence(object *objp)
- {
- if (Robot_info[objp->id].boss_flag) {
- Boss_dying = 1;
- Boss_dying_start_time = GameTime;
- }
- }
- // ----------------------------------------------------------------------
- void do_boss_dying_frame(object *objp)
- {
- fix boss_roll_val, temp;
- boss_roll_val = fixdiv(GameTime - Boss_dying_start_time, BOSS_DEATH_DURATION);
- fix_sincos(fixmul(boss_roll_val, boss_roll_val), &temp, &objp->mtype.phys_info.rotvel.x);
- fix_sincos(boss_roll_val, &temp, &objp->mtype.phys_info.rotvel.y);
- fix_sincos(boss_roll_val-F1_0/8, &temp, &objp->mtype.phys_info.rotvel.z);
- objp->mtype.phys_info.rotvel.x = (GameTime - Boss_dying_start_time)/9;
- objp->mtype.phys_info.rotvel.y = (GameTime - Boss_dying_start_time)/5;
- objp->mtype.phys_info.rotvel.z = (GameTime - Boss_dying_start_time)/7;
- if (Boss_dying_start_time + BOSS_DEATH_DURATION - BOSS_DEATH_SOUND_DURATION < GameTime) {
- if (!Boss_dying_sound_playing) {
- mprintf((0, "Starting boss death sound!\n"));
- Boss_dying_sound_playing = 1;
- 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
- } else if (rand() < FrameTime*16)
- create_small_fireball_on_object(objp, (F1_0 + rand()) * 8, 0);
- } else if (rand() < FrameTime*8)
- create_small_fireball_on_object(objp, (F1_0/2 + rand()) * 8, 1);
- if (Boss_dying_start_time + BOSS_DEATH_DURATION < GameTime) {
- do_controlcen_destroyed_stuff(NULL);
- explode_object(objp, F1_0/4);
- digi_link_sound_to_object2(SOUND_BADASS_EXPLOSION, objp-Objects, 0, F2_0, F1_0*512);
- }
- }
- #ifndef SHAREWARE
- #ifdef NETWORK
- // --------------------------------------------------------------------------------------------------------------------
- // Called for an AI object if it is fairly aware of the player.
- // awareness_level is in 0..100. Larger numbers indicate greater awareness (eg, 99 if firing at player).
- // In a given frame, might not get called for an object, or might be called more than once.
- // The fact that this routine is not called for a given object does not mean that object is not interested in the player.
- // Objects are moved by physics, so they can move even if not interested in a player. However, if their velocity or
- // orientation is changing, this routine will be called.
- // Return value:
- // 0 this player IS NOT allowed to move this robot.
- // 1 this player IS allowed to move this robot.
- int ai_multiplayer_awareness(object *objp, int awareness_level)
- {
- int rval=1;
- #ifndef SHAREWARE
- if (Game_mode & GM_MULTI) {
- if (awareness_level == 0)
- return 0;
- rval = multi_can_move_robot(objp-Objects, awareness_level);
- }
- #endif
- return rval;
- }
- #else
- #define ai_multiplayer_awareness(a, b) 1
- #endif
- #else
- #define ai_multiplayer_awareness(a, b) 1
- #endif
- #ifndef NDEBUG
- fix Prev_boss_shields = -1;
- #endif
- // --------------------------------------------------------------------------------------------------------------------
- // Do special stuff for a boss.
- void do_boss_stuff(object *objp)
- {
- // New code, fixes stupid bug which meant boss never gated in robots if > 32767 seconds played.
- if (Last_teleport_time > GameTime)
- Last_teleport_time = GameTime;
- if (Last_gate_time > GameTime)
- Last_gate_time = GameTime;
- #ifndef NDEBUG
- if (objp->shields != Prev_boss_shields) {
- mprintf((0, "Boss shields = %7.3f, object %i\n", f2fl(objp->shields), objp-Objects));
- Prev_boss_shields = objp->shields;
- }
- #endif
- if (!Boss_dying) {
- if (objp->ctype.ai_info.CLOAKED == 1) {
- 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)) {
- if (ai_multiplayer_awareness(objp, 98))
- teleport_boss(objp);
- } else if (Boss_hit_this_frame) {
- Boss_hit_this_frame = 0;
- Last_teleport_time -= Boss_teleport_interval/4;
- }
- if (GameTime > Boss_cloak_end_time)
- objp->ctype.ai_info.CLOAKED = 0;
- } else {
- if ((GameTime - Boss_cloak_end_time > Boss_cloak_interval) || Boss_hit_this_frame) {
- if (ai_multiplayer_awareness(objp, 95))
- {
- Boss_hit_this_frame = 0;
- Boss_cloak_start_time = GameTime;
- Boss_cloak_end_time = GameTime+Boss_cloak_duration;
- objp->ctype.ai_info.CLOAKED = 1;
- #ifndef SHAREWARE
- #ifdef NETWORK
- if (Game_mode & GM_MULTI)
- multi_send_boss_actions(objp-Objects, 2, 0, 0);
- #endif
- #endif
- }
- }
- }
- } else
- do_boss_dying_frame(objp);
- }
- #define BOSS_TO_PLAYER_GATE_DISTANCE (F1_0*150)
- #ifndef SHAREWARE
- // --------------------------------------------------------------------------------------------------------------------
- // Do special stuff for a boss.
- void do_super_boss_stuff(object *objp, fix dist_to_player, int player_visibility)
- {
- static int eclip_state = 0;
- do_boss_stuff(objp);
- // Only master player can cause gating to occur.
- #ifdef NETWORK
- if ((Game_mode & GM_MULTI) && !network_i_am_master())
- return;
- #endif
- if ((dist_to_player < BOSS_TO_PLAYER_GATE_DISTANCE) || player_visibility || (Game_mode & GM_MULTI)) {
- if (GameTime - Last_gate_time > Gate_interval/2) {
- restart_effect(BOSS_ECLIP_NUM);
- #ifndef SHAREWARE
- #ifdef NETWORK
- if (eclip_state == 0) {
- multi_send_boss_actions(objp-Objects, 4, 0, 0);
- eclip_state = 1;
- }
- #endif
- #endif
- }
- else {
- stop_effect(BOSS_ECLIP_NUM);
- #ifndef SHAREWARE
- #ifdef NETWORK
- if (eclip_state == 1) {
- multi_send_boss_actions(objp-Objects, 5, 0, 0);
- eclip_state = 0;
- }
- #endif
- #endif
- }
- if (GameTime - Last_gate_time > Gate_interval)
- if (ai_multiplayer_awareness(objp, 99)) {
- int rtval;
- int randtype = (rand() * MAX_GATE_INDEX) >> 15;
- Assert(randtype < MAX_GATE_INDEX);
- randtype = Super_boss_gate_list[randtype];
- Assert(randtype < N_robot_types);
- rtval = gate_in_robot(randtype, -1);
- #ifndef SHAREWARE
- #ifdef NETWORK
- if (rtval && (Game_mode & GM_MULTI))
- {
- multi_send_boss_actions(objp-Objects, 3, randtype, Net_create_objnums[0]);
- map_objnum_local_to_local(Net_create_objnums[0]);
- }
- #endif
- #endif
- }
- }
- }
- #endif
- //int multi_can_move_robot(object *objp, int awareness_level)
- //{
- // return 0;
- //}
- #ifndef SHAREWARE
- void ai_multi_send_robot_position(int objnum, int force)
- {
- #ifndef SHAREWARE
- #ifdef NETWORK
- if (Game_mode & GM_MULTI)
- {
- if (force != -1)
- multi_send_robot_position(objnum, 1);
- else
- multi_send_robot_position(objnum, 0);
- }
- #endif
- #endif
- return;
- }
- #else
- #define ai_multi_send_robot_position(a, b)
- #endif
- // --------------------------------------------------------------------------------------------------------------------
- // Returns true if this object should be allowed to fire at the player.
- int maybe_ai_do_actual_firing_stuff(object *obj, ai_static *aip)
- {
- if (Game_mode & GM_MULTI)
- if ((aip->GOAL_STATE != AIS_FLIN) && (obj->id != ROBOT_BRAIN))
- if (aip->CURRENT_STATE == AIS_FIRE)
- return 1;
- return 0;
- }
- // --------------------------------------------------------------------------------------------------------------------
- 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)
- {
- fix dot;
- if (player_visibility == 2) {
- // Changed by mk, 01/04/94, onearm would take about 9 seconds until he can fire at you.
- // if (((!object_animates) || (ailp->achieved_state[aip->CURRENT_GUN] == AIS_FIRE)) && (ailp->next_fire <= 0)) {
- if (!object_animates || (ailp->next_fire <= 0)) {
- dot = vm_vec_dot(&obj->orient.fvec, vec_to_player);
- if (dot >= 7*F1_0/8) {
- if (aip->CURRENT_GUN < Robot_info[obj->id].n_guns) {
- if (robptr->attack_type == 1) {
- if (!Player_exploded && (dist_to_player < obj->size + ConsoleObject->size + F1_0*2)) { // robptr->circle_distance[Difficulty_level] + ConsoleObject->size) {
- if (!ai_multiplayer_awareness(obj, ROBOT_FIRE_AGITATION-2))
- return;
- do_ai_robot_hit_attack(obj, ConsoleObject, &obj->pos);
- } else {
- // 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)));
- return;
- }
- } else {
- if ((gun_point->x == 0) && (gun_point->y == 0) && (gun_point->z == 0)) {
- ; //mprintf((0, "Would like to fire gun, but gun not selected.\n"));
- } else {
- if (!ai_multiplayer_awareness(obj, ROBOT_FIRE_AGITATION))
- return;
- ai_fire_laser_at_player(obj, gun_point);
- }
- }
- // Wants to fire, so should go into chase mode, probably.
- if ( (aip->behavior != AIB_RUN_FROM) && (aip->behavior != AIB_STILL) && (aip->behavior != AIB_FOLLOW_PATH) && ((ailp->mode == AIM_FOLLOW_PATH) || (ailp->mode == AIM_STILL)))
- ailp->mode = AIM_CHASE_OBJECT;
- }
- aip->GOAL_STATE = AIS_RECO;
- ailp->goal_state[aip->CURRENT_GUN] = AIS_RECO;
- // Switch to next gun for next fire.
- aip->CURRENT_GUN++;
- if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
- aip->CURRENT_GUN = 0;
- }
- }
- } else if (Weapon_info[Robot_info[obj->id].weapon_type].homing_flag == 1) {
- // Robots which fire homing weapons might fire even if they don't have a bead on the player.
- 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)) {
- if (!ai_multiplayer_awareness(obj, ROBOT_FIRE_AGITATION))
- return;
- ai_fire_laser_at_player(obj, gun_point);
- aip->GOAL_STATE = AIS_RECO;
- ailp->goal_state[aip->CURRENT_GUN] = AIS_RECO;
- // Switch to next gun for next fire.
- aip->CURRENT_GUN++;
- if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
- aip->CURRENT_GUN = 0;
- } else {
- // Switch to next gun for next fire.
- aip->CURRENT_GUN++;
- if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
- aip->CURRENT_GUN = 0;
- }
- }
- }
- // --------------------------------------------------------------------------------------------------------------------
- void do_ai_frame(object *obj)
- {
- int objnum = obj-Objects;
- ai_static *aip = &obj->ctype.ai_info;
- ai_local *ailp = &Ai_local_info[objnum];
- fix dist_to_player;
- vms_vector vec_to_player;
- fix dot;
- robot_info *robptr;
- int player_visibility=-1;
- int obj_ref;
- int object_animates;
- int new_goal_state;
- int visibility_and_vec_computed = 0;
- int previous_visibility;
- vms_vector gun_point;
- vms_vector vis_vec_pos;
- if (aip->SKIP_AI_COUNT) {
- aip->SKIP_AI_COUNT--;
- return;
- }
- // Kind of a hack. If a robot is flinching, but it is time for it to fire, unflinch it.
- // Else, you can turn a big nasty robot into a wimp by firing flares at it.
- // This also allows the player to see the cool flinch effect for mechs without unbalancing the game.
- if ((aip->GOAL_STATE == AIS_FLIN) && (ailp->next_fire < 0)) {
- aip->GOAL_STATE = AIS_FIRE;
- }
- #ifndef NDEBUG
- if ((aip->behavior == AIB_RUN_FROM) && (ailp->mode != AIM_RUN_FROM_OBJECT))
- Int3(); // This is peculiar. Behavior is run from, but mode is not. Contact Mike.
- mprintf_animation_info((obj));
- if (Ai_animation_test) {
- if (aip->GOAL_STATE == aip->CURRENT_STATE) {
- aip->GOAL_STATE++;
- if (aip->GOAL_STATE > AIS_RECO)
- aip->GOAL_STATE = AIS_REST;
- }
- mprintf((0, "Frame %4i, current = %i, goal = %i\n", FrameCount, aip->CURRENT_STATE, aip->GOAL_STATE));
- object_animates = do_silly_animation(obj);
- if (object_animates)
- ai_frame_animation(obj);
- return;
- }
- if (!Do_ai_flag)
- return;
- if (Break_on_object != -1)
- if ((obj-Objects) == Break_on_object)
- Int3(); // Contact Mike: This is a debug break
- #endif
- Believed_player_pos = Ai_cloak_info[objnum & (MAX_AI_CLOAK_INFO-1)].last_position;
- // 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)));
- // 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));
- // Assert((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR));
- if (!((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR))) {
- // mprintf((0, "Object %i behavior is %i, setting to AIB_NORMAL, fix in editor!\n", objnum, aip->behavior));
- aip->behavior = AIB_NORMAL;
- }
- Assert(obj->segnum != -1);
- Assert(obj->id < N_robot_types);
- robptr = &Robot_info[obj->id];
- Assert(robptr->always_0xabcd == 0xabcd);
- obj_ref = objnum ^ FrameCount;
- // -- if (ailp->wait_time > -F1_0*8)
- // -- ailp->wait_time -= FrameTime;
- if (ailp->next_fire > -F1_0*8)
- ailp->next_fire -= FrameTime;
- if (ailp->time_since_processed < F1_0*256)
- ailp->time_since_processed += FrameTime;
- previous_visibility = ailp->previous_visibility; // Must get this before we toast the master copy!
- // Deal with cloaking for robots which are cloaked except just before firing.
- if (robptr->cloak_type == RI_CLOAKED_EXCEPT_FIRING)
- if (ailp->next_fire < F1_0/2)
- aip->CLOAKED = 1;
- else
- aip->CLOAKED = 0;
- if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
- Believed_player_pos = ConsoleObject->pos;
- dist_to_player = vm_vec_dist_quick(&Believed_player_pos, &obj->pos);
- //--!-- 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)));
- //--!-- if (ailp->mode == AIM_FOLLOW_PATH) {
- //--!-- mprintf((0, "gseg = %i\n", Point_segs[aip->hide_index+aip->cur_path_index].segnum));
- //--!-- } else
- //--!-- mprintf((0, "\n"));
- // If this robot can fire, compute visibility from gun position.
- // Don't want to compute visibility twice, as it is expensive. (So is call to calc_gun_point).
- if ((ailp->next_fire <= 0) && (dist_to_player < F1_0*200) && (robptr->n_guns) && !(robptr->attack_type)) {
- calc_gun_point(&gun_point, obj, aip->CURRENT_GUN);
- vis_vec_pos = gun_point;
- // mprintf((0, "Visibility = %i, computed from gun #%i\n", player_visibility, aip->CURRENT_GUN));
- } else {
- vis_vec_pos = obj->pos;
- vm_vec_zero(&gun_point);
- // mprintf((0, "Visibility = %i, computed from center.\n", player_visibility));
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // Occasionally make non-still robots make a path to the player. Based on agitation and distance from player.
- if ((aip->behavior != AIB_RUN_FROM) && (aip->behavior != AIB_STILL) && !(Game_mode & GM_MULTI))
- if (Overall_agitation > 70) {
- if ((dist_to_player < F1_0*200) && (rand() < FrameTime/4)) {
- if (rand() * (Overall_agitation - 40) > F1_0*5) {
- // -- mprintf((0, "(1) Object #%i going from still to path in frame %i.\n", objnum, FrameCount));
- create_path_to_player(obj, 4 + Overall_agitation/8 + Difficulty_level, 1);
- // -- show_path_and_other(obj);
- return;
- }
- }
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // If retry count not 0, then add it into consecutive_retries.
- // If it is 0, cut down consecutive_retries.
- // This is largely a hack to speed up physics and deal with stupid AI. This is low level
- // communication between systems of a sort that should not be done.
- if ((ailp->retry_count) && !(Game_mode & GM_MULTI)) {
- ailp->consecutive_retries += ailp->retry_count;
- ailp->retry_count = 0;
- if (ailp->consecutive_retries > 3) {
- switch (ailp->mode) {
- case AIM_CHASE_OBJECT:
- // -- mprintf((0, "(2) Object #%i, retries while chasing, creating path to player in frame %i\n", objnum, FrameCount));
- create_path_to_player(obj, 4 + Overall_agitation/8 + Difficulty_level, 1);
- break;
- case AIM_STILL:
- if (!((aip->behavior == AIB_STILL) || (aip->behavior == AIB_STATION))) // Behavior is still, so don't follow path.
- attempt_to_resume_path(obj);
- break;
- case AIM_FOLLOW_PATH:
- // mprintf((0, "Object %i following path got %i retries in frame %i\n", obj-Objects, ailp->consecutive_retries, FrameCount));
- if (Game_mode & GM_MULTI)
- ailp->mode = AIM_STILL;
- else
- attempt_to_resume_path(obj);
- break;
- case AIM_RUN_FROM_OBJECT:
- move_towards_segment_center(obj);
- obj->mtype.phys_info.velocity.x = 0;
- obj->mtype.phys_info.velocity.y = 0;
- obj->mtype.phys_info.velocity.z = 0;
- create_n_segment_path(obj, 5, -1);
- ailp->mode = AIM_RUN_FROM_OBJECT;
- break;
- case AIM_HIDE:
- move_towards_segment_center(obj);
- obj->mtype.phys_info.velocity.x = 0;
- obj->mtype.phys_info.velocity.y = 0;
- obj->mtype.phys_info.velocity.z = 0;
- // -- mprintf((0, "Hiding, yet creating path to player.\n"));
- if (Overall_agitation > (50 - Difficulty_level*4))
- create_path_to_player(obj, 4 + Overall_agitation/8, 1);
- else {
- create_n_segment_path(obj, 5, -1);
- }
- break;
- case AIM_OPEN_DOOR:
- create_n_segment_path_to_door(obj, 5, -1);
- break;
- #ifndef NDEBUG
- case AIM_FOLLOW_PATH_2:
- Int3(); // Should never happen!
- break;
- #endif
- }
- ailp->consecutive_retries = 0;
- }
- } else
- ailp->consecutive_retries /= 2;
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // If in materialization center, exit
- if (!(Game_mode & GM_MULTI) && (Segments[obj->segnum].special == SEGMENT_IS_ROBOTMAKER)) {
- ai_follow_path(obj, 1); // 1 = player is visible, which might be a lie, but it works.
- return;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // Decrease player awareness due to the passage of time.
- //-----old, faster way from about 11/06/94----- if (ailp->player_awareness_type) {
- //-----old, faster way from about 11/06/94----- if (ailp->player_awareness_time > 0) {
- //-----old, faster way from about 11/06/94----- ailp->player_awareness_time -= FrameTime;
- //-----old, faster way from about 11/06/94----- if (ailp->player_awareness_time <= 0) {
- //-----old, faster way from about 11/06/94----- ailp->player_awareness_time = F1_0*2;
- //-----old, faster way from about 11/06/94----- ailp->player_awareness_type--;
- //-----old, faster way from about 11/06/94----- if (ailp->player_awareness_type < 0) {
- //-----old, faster way from about 11/06/94----- aip->GOAL_STATE = AIS_REST;
- //-----old, faster way from about 11/06/94----- ailp->player_awareness_type = 0;
- //-----old, faster way from about 11/06/94----- }
- //-----old, faster way from about 11/06/94-----
- //-----old, faster way from about 11/06/94----- }
- //-----old, faster way from about 11/06/94----- } else {
- //-----old, faster way from about 11/06/94----- ailp->player_awareness_type = 0;
- //-----old, faster way from about 11/06/94----- aip->GOAL_STATE = AIS_REST;
- //-----old, faster way from about 11/06/94----- }
- //-----old, faster way from about 11/06/94----- } else
- //-----old, faster way from about 11/06/94----- aip->GOAL_STATE = AIS_REST;
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // Decrease player awareness due to the passage of time.
- if (ailp->player_awareness_type) {
- if (ailp->player_awareness_time > 0) {
- ailp->player_awareness_time -= FrameTime;
- if (ailp->player_awareness_time <= 0) {
- ailp->player_awareness_time = F1_0*2; //new: 11/05/94
- ailp->player_awareness_type--; //new: 11/05/94
- }
- } else {
- ailp->player_awareness_type--;
- ailp->player_awareness_time = F1_0*2;
- // aip->GOAL_STATE = AIS_REST;
- }
- } else
- aip->GOAL_STATE = AIS_REST; //new: 12/13/94
- if (Player_is_dead && (ailp->player_awareness_type == 0))
- if ((dist_to_player < F1_0*200) && (rand() < FrameTime/8)) {
- if ((aip->behavior != AIB_STILL) && (aip->behavior != AIB_RUN_FROM)) {
- if (!ai_multiplayer_awareness(obj, 30))
- return;
- ai_multi_send_robot_position(objnum, -1);
- if (!((ailp->mode == AIM_FOLLOW_PATH) && (aip->cur_path_index < aip->path_length-1)))
- if (dist_to_player < F1_0*30)
- create_n_segment_path(obj, 5, 1);
- else
- create_path_to_player(obj, 20, 1);
- }
- }
- // Make sure that if this guy got hit or bumped, then he's chasing player.
- if ((ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION) || (ailp->player_awareness_type >= PA_PLAYER_COLLISION)) {
- if ((aip->behavior != AIB_STILL) && (aip->behavior != AIB_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM) && (obj->id != ROBOT_BRAIN))
- ailp->mode = AIM_CHASE_OBJECT;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- if ((aip->GOAL_STATE == AIS_FLIN) && (aip->CURRENT_STATE == AIS_FLIN))
- aip->GOAL_STATE = AIS_LOCK;
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // Note: Should only do these two function calls for objects which animate
- if ((dist_to_player < F1_0*100)) { // && !(Game_mode & GM_MULTI)) {
- object_animates = do_silly_animation(obj);
- if (object_animates)
- ai_frame_animation(obj);
- //mprintf((0, "Object %i: goal=%i, current=%i\n", obj-Objects, obj->ctype.ai_info.GOAL_STATE, obj->ctype.ai_info.CURRENT_STATE));
- } else {
- // If Object is supposed to animate, but we don't let it animate due to distance, then
- // we must change its state, else it will never update.
- aip->CURRENT_STATE = aip->GOAL_STATE;
- object_animates = 0; // If we're not doing the animation, then should pretend it doesn't animate.
- }
- //Processed_this_frame++;
- //if (FrameCount != LastFrameCount) {
- // LastFrameCount = FrameCount;
- // mprintf((0, "Processed in frame %i = %i robots\n", FrameCount-1, Processed_this_frame));
- // Processed_this_frame = 0;
- //}
- switch (Robot_info[obj->id].boss_flag) {
- case 0:
- break;
- case 1:
- if (aip->GOAL_STATE == AIS_FLIN)
- aip->GOAL_STATE = AIS_FIRE;
- if (aip->CURRENT_STATE == AIS_FLIN)
- aip->CURRENT_STATE = AIS_FIRE;
- dist_to_player /= 4;
- do_boss_stuff(obj);
- dist_to_player *= 4;
- break;
- #ifndef SHAREWARE
- case 2:
- if (aip->GOAL_STATE == AIS_FLIN)
- aip->GOAL_STATE = AIS_FIRE;
- if (aip->CURRENT_STATE == AIS_FLIN)
- aip->CURRENT_STATE = AIS_FIRE;
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- { int pv = player_visibility;
- fix dtp = dist_to_player/4;
- // If player cloaked, visibility is screwed up and superboss will gate in robots when not supposed to.
- if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
- pv = 0;
- dtp = vm_vec_dist_quick(&ConsoleObject->pos, &obj->pos)/4;
- }
- do_super_boss_stuff(obj, dtp, pv);
- }
- break;
- #endif
- default:
- Int3(); // Bogus boss flag value.
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // Time-slice, don't process all the time, purely an efficiency hack.
- // Guys whose behavior is station and are not at their hide segment get processed anyway.
- if (ailp->player_awareness_type < PA_WEAPON_ROBOT_COLLISION-1) { // If robot got hit, he gets to attack player always!
- #ifndef NDEBUG
- if (Break_on_object != objnum) { // don't time slice if we're interested in this object.
- #endif
- if ((dist_to_player > F1_0*250) && (ailp->time_since_processed <= F1_0*2))
- return;
- else if (!((aip->behavior == AIB_STATION) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->hide_segment != obj->segnum))) {
- if ((dist_to_player > F1_0*150) && (ailp->time_since_processed <= F1_0))
- return;
- else if ((dist_to_player > F1_0*100) && (ailp->time_since_processed <= F1_0/2))
- return;
- }
- #ifndef NDEBUG
- }
- #endif
- }
- // -- if ((aip->behavior == AIB_STATION) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->hide_segment != obj->segnum))
- // -- mprintf((0, "[%i] ", obj-Objects));
- // Reset time since processed, but skew objects so not everything processed synchronously, else
- // we get fast frames with the occasional very slow frame.
- // AI_proc_time = ailp->time_since_processed;
- ailp->time_since_processed = - ((objnum & 0x03) * FrameTime ) / 2;
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // Perform special ability
- switch (obj->id) {
- case ROBOT_BRAIN:
- // Robots function nicely if behavior is Station. This means they won't move until they
- // can see the player, at which time they will start wandering about opening doors.
- if (ConsoleObject->segnum == obj->segnum) {
- if (!ai_multiplayer_awareness(obj, 97))
- return;
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- move_away_from_player(obj, &vec_to_player, 0);
- ai_multi_send_robot_position(objnum, -1);
- } else if (ailp->mode != AIM_STILL) {
- int r;
- r = openable_doors_in_segment(obj);
- if (r != -1) {
- ailp->mode = AIM_OPEN_DOOR;
- aip->GOALSIDE = r;
- } else if (ailp->mode != AIM_FOLLOW_PATH) {
- if (!ai_multiplayer_awareness(obj, 50))
- return;
- create_n_segment_path_to_door(obj, 8+Difficulty_level, -1); // third parameter is avoid_seg, -1 means avoid nothing.
- ai_multi_send_robot_position(objnum, -1);
- }
- } else {
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- if (player_visibility) {
- if (!ai_multiplayer_awareness(obj, 50))
- return;
- create_n_segment_path_to_door(obj, 8+Difficulty_level, -1); // third parameter is avoid_seg, -1 means avoid nothing.
- ai_multi_send_robot_position(objnum, -1);
- }
- }
- break;
- default:
- break;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- switch (ailp->mode) {
- case AIM_CHASE_OBJECT: { // chasing player, sort of, chase if far, back off if close, circle in between
- fix circle_distance;
- circle_distance = robptr->circle_distance[Difficulty_level] + ConsoleObject->size;
- // Green guy doesn't get his circle distance boosted, else he might never attack.
- if (robptr->attack_type != 1)
- circle_distance += (objnum&0xf) * F1_0/2;
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- // @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.
- if ((player_visibility < 2) && (previous_visibility == 2)) { // this is redundant: mk, 01/15/95: && (ailp->mode == AIM_CHASE_OBJECT)) {
- // mprintf((0, "I used to be able to see the player!\n"));
- if (!ai_multiplayer_awareness(obj, 53)) {
- if (maybe_ai_do_actual_firing_stuff(obj, aip))
- ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
- return;
- }
- // -- mprintf((0, "(3) Object #%i going from chase to player path in frame %i.\n", objnum, FrameCount));
- create_path_to_player(obj, 8, 1);
- // -- show_path_and_other(obj);
- ai_multi_send_robot_position(objnum, -1);
- } else if ((player_visibility == 0) && (dist_to_player > F1_0*80) && (!(Game_mode & GM_MULTI))) {
- // If pretty far from the player, player cannot be seen (obstructed) and in chase mode, switch to follow path mode.
- // This has one desirable benefit of avoiding physics retries.
- if (aip->behavior == AIB_STATION) {
- ailp->goal_segment = aip->hide_segment;
- // -- mprintf((0, "(1) Object #%i going from chase to STATION in frame %i.\n", objnum, FrameCount));
- create_path_to_station(obj, 15);
- // -- show_path_and_other(obj);
- } else
- create_n_segment_path(obj, 5, -1);
- break;
- }
- if ((aip->CURRENT_STATE == AIS_REST) && (aip->GOAL_STATE == AIS_REST)) {
- if (player_visibility) {
- if (rand() < FrameTime*player_visibility) {
- if (dist_to_player/256 < rand()*player_visibility) {
- // mprintf((0, "Object %i searching for player.\n", obj-Objects));
- aip->GOAL_STATE = AIS_SRCH;
- aip->CURRENT_STATE = AIS_SRCH;
- }
- }
- }
- }
- if (GameTime - ailp->time_player_seen > CHASE_TIME_LENGTH) {
- if (Game_mode & GM_MULTI)
- if (!player_visibility && (dist_to_player > F1_0*70)) {
- ailp->mode = AIM_STILL;
- return;
- }
- if (!ai_multiplayer_awareness(obj, 64)) {
- if (maybe_ai_do_actual_firing_stuff(obj, aip))
- ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
- return;
- }
- // -- mprintf((0, "(4) Object #%i going from chase to player path in frame %i.\n", objnum, FrameCount));
- create_path_to_player(obj, 10, 1);
- // -- show_path_and_other(obj);
- ai_multi_send_robot_position(objnum, -1);
- } else if ((aip->CURRENT_STATE != AIS_REST) && (aip->GOAL_STATE != AIS_REST)) {
- if (!ai_multiplayer_awareness(obj, 70)) {
- if (maybe_ai_do_actual_firing_stuff(obj, aip))
- ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
- return;
- }
- ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, circle_distance, 0);
- if ((obj_ref & 1) && ((aip->GOAL_STATE == AIS_SRCH) || (aip->GOAL_STATE == AIS_LOCK))) {
- if (player_visibility) // == 2)
- ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
- else
- ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
- }
- if (ai_evaded) {
- ai_multi_send_robot_position(objnum, 1);
- ai_evaded = 0;
- }
- else
- ai_multi_send_robot_position(objnum, -1);
-
- do_firing_stuff(obj, player_visibility, &vec_to_player);
- }
- break;
- }
- case AIM_RUN_FROM_OBJECT:
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- if (player_visibility) {
- if (ailp->player_awareness_type == 0)
- ailp->player_awareness_type = PA_WEAPON_ROBOT_COLLISION;
- }
- // If in multiplayer, only do if player visible. If not multiplayer, do always.
- if (!(Game_mode & GM_MULTI) || player_visibility)
- if (ai_multiplayer_awareness(obj, 75)) {
- ai_follow_path(obj, player_visibility);
- ai_multi_send_robot_position(objnum, -1);
- }
- if (aip->GOAL_STATE != AIS_FLIN)
- aip->GOAL_STATE = AIS_LOCK;
- else if (aip->CURRENT_STATE == AIS_FLIN)
- aip->GOAL_STATE = AIS_LOCK;
- // Bad to let run_from robot fire at player because it will cause a war in which it turns towards the
- // player to fire and then towards its goal to move.
- // do_firing_stuff(obj, player_visibility, &vec_to_player);
- // Instead, do this:
- // (Note, only drop if player is visible. This prevents the bombs from being a giveaway, and
- // also ensures that the robot is moving while it is dropping. Also means fewer will be dropped.)
- if ((ailp->next_fire <= 0) && (player_visibility)) {
- vms_vector fire_vec, fire_pos;
- if (!ai_multiplayer_awareness(obj, 75))
- return;
- fire_vec = obj->orient.fvec;
- vm_vec_negate(&fire_vec);
- vm_vec_add(&fire_pos, &obj->pos, &fire_vec);
- Laser_create_new_easy( &fire_vec, &fire_pos, obj-Objects, PROXIMITY_ID, 1);
- ailp->next_fire = F1_0*5; // Drop a proximity bomb every 5 seconds.
-
- #ifdef NETWORK
- if (Game_mode & GM_MULTI)
- {
- ai_multi_send_robot_position(obj-Objects, -1);
- multi_send_robot_fire(obj-Objects, -1, &fire_vec);
- }
- #endif
- }
- break;
- case AIM_FOLLOW_PATH: {
- int anger_level = 65;
- if (aip->behavior == AIB_STATION)
- if (Point_segs[aip->hide_index + aip->path_length - 1].segnum == aip->hide_segment) {
- anger_level = 64;
- // mprintf((0, "Object %i, station, lowering anger to 64.\n"));
- }
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- if (Game_mode & (GM_MODEM | GM_SERIAL))
- if (!player_visibility && (dist_to_player > F1_0*70)) {
- ailp->mode = AIM_STILL;
- return;
- }
- if (!ai_multiplayer_awareness(obj, anger_level)) {
- if (maybe_ai_do_actual_firing_stuff(obj, aip)) {
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
- }
- return;
- }
- ai_follow_path(obj, player_visibility);
- if (aip->GOAL_STATE != AIS_FLIN)
- aip->GOAL_STATE = AIS_LOCK;
- else if (aip->CURRENT_STATE == AIS_FLIN)
- aip->GOAL_STATE = AIS_LOCK;
- if ((aip->behavior != AIB_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM))
- do_firing_stuff(obj, player_visibility, &vec_to_player);
- if ((player_visibility == 2) && (aip->behavior != AIB_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM) && (obj->id != ROBOT_BRAIN)) {
- if (robptr->attack_type == 0)
- ailp->mode = AIM_CHASE_OBJECT;
- } else if ((player_visibility == 0) && (aip->behavior == AIB_NORMAL) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM)) {
- ailp->mode = AIM_STILL;
- aip->hide_index = -1;
- aip->path_length = 0;
- }
- ai_multi_send_robot_position(objnum, -1);
- break;
- }
- case AIM_HIDE:
- if (!ai_multiplayer_awareness(obj, 71)) {
- if (maybe_ai_do_actual_firing_stuff(obj, aip)) {
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
- }
- return;
- }
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- ai_follow_path(obj, player_visibility);
- if (aip->GOAL_STATE != AIS_FLIN)
- aip->GOAL_STATE = AIS_LOCK;
- else if (aip->CURRENT_STATE == AIS_FLIN)
- aip->GOAL_STATE = AIS_LOCK;
- ai_multi_send_robot_position(objnum, -1);
- break;
- case AIM_STILL:
- if ((dist_to_player < F1_0*120+Difficulty_level*F1_0*20) || (ailp->player_awareness_type >= PA_WEAPON_ROBOT_COLLISION-1)) {
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- // turn towards vector if visible this time or last time, or rand
- // new!
- if ((player_visibility) || (previous_visibility) || ((rand() > 0x4000) && !(Game_mode & GM_MULTI))) {
- if (!ai_multiplayer_awareness(obj, 71)) {
- if (maybe_ai_do_actual_firing_stuff(obj, aip))
- ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
- return;
- }
- ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
- ai_multi_send_robot_position(objnum, -1);
- }
- do_firing_stuff(obj, player_visibility, &vec_to_player);
- // This is debugging code! Remove it! It's to make the green guy attack without doing other kinds of movement.
- 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.
- if (robptr->attack_type == 1) {
- aip->behavior = AIB_NORMAL;
- if (!ai_multiplayer_awareness(obj, 80)) {
- if (maybe_ai_do_actual_firing_stuff(obj, aip))
- ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
- return;
- }
- ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, 0, 0);
- if (ai_evaded) {
- ai_multi_send_robot_position(objnum, 1);
- ai_evaded = 0;
- }
- else
- ai_multi_send_robot_position(objnum, -1);
- } else {
- // Robots in hover mode are allowed to evade at half normal speed.
- if (!ai_multiplayer_awareness(obj, 81)) {
- if (maybe_ai_do_actual_firing_stuff(obj, aip))
- ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
- return;
- }
- ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, 0, 1);
- if (ai_evaded) {
- ai_multi_send_robot_position(objnum, -1);
- ai_evaded = 0;
- }
- else
- ai_multi_send_robot_position(objnum, -1);
- }
- } else if ((obj->segnum != aip->hide_segment) && (dist_to_player > F1_0*80) && (!(Game_mode & GM_MULTI))) {
- // If pretty far from the player, player cannot be seen (obstructed) and in chase mode, switch to follow path mode.
- // This has one desirable benefit of avoiding physics retries.
- if (aip->behavior == AIB_STATION) {
- ailp->goal_segment = aip->hide_segment;
- // -- mprintf((0, "(2) Object #%i going from STILL to STATION in frame %i.\n", objnum, FrameCount));
- create_path_to_station(obj, 15);
- // -- show_path_and_other(obj);
- }
- break;
- }
- }
- break;
- case AIM_OPEN_DOOR: { // trying to open a door.
- vms_vector center_point, goal_vector;
- Assert(obj->id == ROBOT_BRAIN); // Make sure this guy is allowed to be in this mode.
- if (!ai_multiplayer_awareness(obj, 62))
- return;
- compute_center_point_on_side(¢er_point, &Segments[obj->segnum], aip->GOALSIDE);
- vm_vec_sub(&goal_vector, ¢er_point, &obj->pos);
- vm_vec_normalize_quick(&goal_vector);
- ai_turn_towards_vector(&goal_vector, obj, robptr->turn_time[Difficulty_level]);
- move_towards_vector(obj, &goal_vector);
- ai_multi_send_robot_position(objnum, -1);
- break;
- }
- default:
- mprintf((0, "Unknown mode = %i in robot %i, behavior = %i\n", ailp->mode, obj-Objects, aip->behavior));
- ailp->mode = AIM_CHASE_OBJECT;
- break;
- } // end: switch (ailp->mode) {
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // If the robot can see you, increase his awareness of you.
- // This prevents the problem of a robot looking right at you but doing nothing.
- // Assert(player_visibility != -1); // Means it didn't get initialized!
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- if (player_visibility == 2)
- if (ailp->player_awareness_type == 0)
- ailp->player_awareness_type = PA_PLAYER_COLLISION;
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- if (!object_animates) {
- aip->CURRENT_STATE = aip->GOAL_STATE;
- // mprintf((0, "Setting current to goal (%i) because object doesn't animate.\n", aip->GOAL_STATE));
- }
- Assert(ailp->player_awareness_type <= AIE_MAX);
- Assert(aip->CURRENT_STATE < AIS_MAX);
- Assert(aip->GOAL_STATE < AIS_MAX);
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- if (ailp->player_awareness_type) {
- new_goal_state = Ai_transition_table[ailp->player_awareness_type-1][aip->CURRENT_STATE][aip->GOAL_STATE];
- if (ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION) {
- // Decrease awareness, else this robot will flinch every frame.
- ailp->player_awareness_type--;
- ailp->player_awareness_time = F1_0*3;
- }
- if (new_goal_state == AIS_ERR_)
- new_goal_state = AIS_REST;
- if (aip->CURRENT_STATE == AIS_NONE)
- aip->CURRENT_STATE = AIS_REST;
- aip->GOAL_STATE = new_goal_state;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // If new state = fire, then set all gun states to fire.
- if ((aip->GOAL_STATE == AIS_FIRE) ) {
- int i,num_guns;
- num_guns = Robot_info[obj->id].n_guns;
- for (i=0; i<num_guns; i++)
- ailp->goal_state[i] = AIS_FIRE;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- // 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
- if ((ailp->next_fire < 0) && (aip->GOAL_STATE == AIS_FIRE))
- aip->CURRENT_STATE = AIS_FIRE;
- if ((aip->GOAL_STATE != AIS_FLIN) && (obj->id != ROBOT_BRAIN)) {
- switch (aip->CURRENT_STATE) {
- case AIS_NONE:
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- dot = vm_vec_dot(&obj->orient.fvec, &vec_to_player);
- if (dot >= F1_0/2)
- if (aip->GOAL_STATE == AIS_REST)
- aip->GOAL_STATE = AIS_SRCH;
- //mprintf((0, "State = none, goal = %i.\n", aip->GOAL_STATE));
- break;
- case AIS_REST:
- if (aip->GOAL_STATE == AIS_REST) {
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- if ((ailp->next_fire <= 0) && (player_visibility)) {
- // mprintf((0, "Setting goal state to fire from rest.\n"));
- aip->GOAL_STATE = AIS_FIRE;
- }
- //mprintf((0, "State = rest, goal = %i.\n", aip->GOAL_STATE));
- }
- break;
- case AIS_SRCH:
- if (!ai_multiplayer_awareness(obj, 60))
- return;
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- if (player_visibility) {
- ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
- ai_multi_send_robot_position(objnum, -1);
- } else if (!(Game_mode & GM_MULTI))
- ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
- break;
- case AIS_LOCK:
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- if (!(Game_mode & GM_MULTI) || (player_visibility)) {
- if (!ai_multiplayer_awareness(obj, 68))
- return;
- if (player_visibility) {
- ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
- ai_multi_send_robot_position(objnum, -1);
- } else if (!(Game_mode & GM_MULTI))
- ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
- }
- break;
- case AIS_FIRE:
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- if (player_visibility) {
- if (!ai_multiplayer_awareness(obj, (ROBOT_FIRE_AGITATION-1)))
- {
- if (Game_mode & GM_MULTI) {
- ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
- return;
- }
- }
- ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
- ai_multi_send_robot_position(objnum, -1);
- } else if (!(Game_mode & GM_MULTI)) {
- ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
- }
- // Fire at player, if appropriate.
- ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
- break;
- case AIS_RECO:
- if (!(obj_ref & 3)) {
- compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
- if (player_visibility) {
- if (!ai_multiplayer_awareness(obj, 69))
- return;
- ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
- ai_multi_send_robot_position(objnum, -1);
- } else if (!(Game_mode & GM_MULTI)) {
- ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
- }
- }
- break;
- case AIS_FLIN:
- // mprintf((0, "State = flinch, goal = %i.\n", aip->GOAL_STATE));
- break;
- default:
- mprintf((1, "Unknown mode for AI object #%i\n", objnum));
- aip->GOAL_STATE = AIS_REST;
- aip->CURRENT_STATE = AIS_REST;
- break;
- }
- } // end of: if (aip->GOAL_STATE != AIS_FLIN) {
- // Switch to next gun for next fire.
- if (player_visibility == 0) {
- aip->CURRENT_GUN++;
- if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
- aip->CURRENT_GUN = 0;
- }
- }
- //--mk, 121094 -- // ----------------------------------------------------------------------------------
- //--mk, 121094 -- void spin_robot(object *robot, vms_vector *collision_point)
- //--mk, 121094 -- {
- //--mk, 121094 -- if (collision_point->x != 3) {
- //--mk, 121094 -- robot->phys_info.rotvel.x = 0x1235;
- //--mk, 121094 -- robot->phys_info.rotvel.y = 0x2336;
- //--mk, 121094 -- robot->phys_info.rotvel.z = 0x3737;
- //--mk, 121094 -- }
- //--mk, 121094 --
- //--mk, 121094 -- }
- // -----------------------------------------------------------------------------------
- void ai_do_cloak_stuff(void)
- {
- int i;
- for (i=0; i<MAX_AI_CLOAK_INFO; i++) {
- Ai_cloak_info[i].last_position = ConsoleObject->pos;
- Ai_cloak_info[i].last_time = GameTime;
- }
- // Make work for control centers.
- Believed_player_pos = Ai_cloak_info[0].last_position;
- }
- // -----------------------------------------------------------------------------------
- // Returns false if awareness is considered too puny to add, else returns true.
- int add_awareness_event(object *objp, int type)
- {
- // If player cloaked and hit a robot, then increase awareness
- if ((type == PA_WEAPON_ROBOT_COLLISION) || (type == PA_WEAPON_WALL_COLLISION) || (type == PA_PLAYER_COLLISION))
- ai_do_cloak_stuff();
- if (Num_awareness_events < MAX_AWARENESS_EVENTS) {
- if ((type == PA_WEAPON_WALL_COLLISION) || (type == PA_WEAPON_ROBOT_COLLISION))
- if (objp->id == VULCAN_ID)
- if (rand() > 3276)
- return 0; // For vulcan cannon, only about 1/10 actually cause awareness
- Awareness_events[Num_awareness_events].segnum = objp->segnum;
- Awareness_events[Num_awareness_events].pos = objp->pos;
- Awareness_events[Num_awareness_events].type = type;
- Num_awareness_events++;
- } else
- Assert(0); // Hey -- Overflowed Awareness_events, make more or something
- // This just gets ignored, so you can just continue.
- return 1;
- }
- // ----------------------------------------------------------------------------------
- // Robots will become aware of the player based on something that occurred.
- // The object (probably player or weapon) which created the awareness is objp.
- void create_awareness_event(object *objp, int type)
- {
- if (add_awareness_event(objp, type)) {
- if (((rand() * (type+4)) >> 15) > 4)
- Overall_agitation++;
- if (Overall_agitation > OVERALL_AGITATION_MAX)
- Overall_agitation = OVERALL_AGITATION_MAX;
- }
- }
- byte New_awareness[MAX_SEGMENTS];
- // ----------------------------------------------------------------------------------
- void pae_aux(int segnum, int type, int level)
- {
- int j;
- if (New_awareness[segnum] < type)
- New_awareness[segnum] = type;
- // Process children.
- if (level <= 4)
- for (j=0; j<MAX_SIDES_PER_SEGMENT; j++)
- if (IS_CHILD(Segments[segnum].children[j]))
- if (type == 4)
- pae_aux(Segments[segnum].children[j], type-1, level+1);
- else
- pae_aux(Segments[segnum].children[j], type, level+1);
- }
- // ----------------------------------------------------------------------------------
- void process_awareness_events(void)
- {
- int i;
- for (i=0; i<=Highest_segment_index; i++)
- New_awareness[i] = 0;
- for (i=0; i<Num_awareness_events; i++)
- pae_aux(Awareness_events[i].segnum, Awareness_events[i].type, 1);
- Num_awareness_events = 0;
- }
- // ----------------------------------------------------------------------------------
- void set_player_awareness_all(void)
- {
- int i;
- process_awareness_events();
- for (i=0; i<=Highest_object_index; i++)
- if (Objects[i].control_type == CT_AI)
- if (New_awareness[Objects[i].segnum] > Ai_local_info[i].player_awareness_type) {
- Ai_local_info[i].player_awareness_type = New_awareness[Objects[i].segnum];
- Ai_local_info[i].player_awareness_time = PLAYER_AWARENESS_INITIAL_TIME;
- }
- }
- #ifndef NDEBUG
- int Ai_dump_enable = 0;
- FILE *Ai_dump_file = NULL;
- char Ai_error_message[128] = "";
- // ----------------------------------------------------------------------------------
- void dump_ai_objects_all()
- {
- #if PARALLAX
- int objnum;
- int total=0;
- time_t time_of_day;
- time_of_day = time(NULL);
- if (!Ai_dump_enable)
- return;
- if (Ai_dump_file == NULL)
- Ai_dump_file = fopen("ai.out","a+t");
- fprintf(Ai_dump_file, "\nnum: seg distance __mode__ behav. [velx vely velz] (Frame = %i)\n", FrameCount);
- fprintf(Ai_dump_file, "Date & Time = %s\n", ctime(&time_of_day));
- if (Ai_error_message[0])
- fprintf(Ai_dump_file, "Error message: %s\n", Ai_error_message);
- for (objnum=0; objnum <= Highest_object_index; objnum++) {
- object *objp = &Objects[objnum];
- ai_static *aip = &objp->ctype.ai_info;
- ai_local *ailp = &Ai_local_info[objnum];
- fix dist_to_player;
- dist_to_player = vm_vec_dist(&objp->pos, &ConsoleObject->pos);
- if (objp->control_type == CT_AI) {
- fprintf(Ai_dump_file, "%3i: %3i %8.3f %8s %8s [%3i %4i]\n",
- objnum, objp->segnum, f2fl(dist_to_player), mode_text[ailp->mode], behavior_text[aip->behavior-0x80], aip->hide_index, aip->path_length);
- if (aip->path_length)
- total += aip->path_length;
- }
- }
- fprintf(Ai_dump_file, "Total path length = %4i\n", total);
- #endif
- }
- // ----------------------------------------------------------------------------------
- void force_dump_ai_objects_all(char *msg)
- {
- int tsave;
- tsave = Ai_dump_enable;
- Ai_dump_enable = 1;
- sprintf(Ai_error_message, "%s\n", msg);
- dump_ai_objects_all();
- Ai_error_message[0] = 0;
- Ai_dump_enable = tsave;
- }
- // ----------------------------------------------------------------------------------
- void turn_off_ai_dump(void)
- {
- if (Ai_dump_file != NULL)
- fclose(Ai_dump_file);
- Ai_dump_file = NULL;
- }
- #endif
- // ----------------------------------------------------------------------------------
- // Do things which need to get done for all AI objects each frame.
- // This includes:
- // Setting player_awareness (a fix, time in seconds which object is aware of player)
- void do_ai_frame_all(void)
- {
- #ifndef NDEBUG
- dump_ai_objects_all();
- #endif
- set_player_awareness_all();
- // if ((FrameCount & 0x07) == 0)
- // mprintf((0, "[%i] ", Overall_agitation));
- }
- //--unused-- // ----------------------------------------------------------------------------------
- //--unused-- // Reset various state information for a new life.
- //--unused-- // Probably not going to be called for a new game because for that an entire object
- //--unused-- // gets reloaded.
- //--unused-- void reset_ai_states(object *objp)
- //--unused-- {
- //--unused-- int i;
- //--unused-- //ai_static *aip = &objp->ctype.ai_info;
- //--unused-- ai_local *ailp = &Ai_local_info[objp-Objects];
- //--unused--
- //--unused-- ailp->wait_time = 0;
- //--unused-- ailp->next_fire = 0;
- //--unused-- ailp->player_awareness_type = 0;
- //--unused-- for (i=0; i<MAX_SUBMODELS; i++) {
- //--unused-- ailp->goal_angles[i].p = 0; ailp->goal_angles[i].b = 0; ailp->goal_angles[i].h = 0;
- //--unused-- ailp->delta_angles[i].p = 0; ailp->delta_angles[i].b = 0; ailp->delta_angles[i].h = 0;
- //--unused-- ailp->goal_state[i] = 0;
- //--unused-- ailp->achieved_state[i] = 0;
- //--unused-- }
- //--unused-- }
- // Initializations to be performed for all robots for a new level.
- void init_robots_for_level(void)
- {
- Overall_agitation = 0;
- }
- int ai_save_state( FILE * fp )
- {
- fwrite( &Ai_initialized, sizeof(int), 1, fp );
- fwrite( &Overall_agitation, sizeof(int), 1, fp );
- fwrite( Ai_local_info, sizeof(ai_local)*MAX_OBJECTS, 1, fp );
- fwrite( Point_segs, sizeof(point_seg)*MAX_POINT_SEGS, 1, fp );
- fwrite( Ai_cloak_info, sizeof(ai_cloak_info)*MAX_AI_CLOAK_INFO, 1, fp );
- fwrite( &Boss_cloak_start_time, sizeof(fix), 1, fp );
- fwrite( &Boss_cloak_end_time , sizeof(fix), 1, fp );
- fwrite( &Last_teleport_time , sizeof(fix), 1, fp );
- fwrite( &Boss_teleport_interval, sizeof(fix), 1, fp );
- fwrite( &Boss_cloak_interval, sizeof(fix), 1, fp );
- fwrite( &Boss_cloak_duration, sizeof(fix), 1, fp );
- fwrite( &Last_gate_time, sizeof(fix), 1, fp );
- fwrite( &Gate_interval, sizeof(fix), 1, fp );
- fwrite( &Boss_dying_start_time, sizeof(fix), 1, fp );
- fwrite( &Boss_dying, sizeof(int), 1, fp );
- fwrite( &Boss_dying_sound_playing, sizeof(int), 1, fp );
- fwrite( &Boss_hit_this_frame, sizeof(int), 1, fp );
- fwrite( &Boss_been_hit, sizeof(int), 1, fp );
- return 1;
- }
- int ai_restore_state( FILE * fp )
- {
- fread( &Ai_initialized, sizeof(int), 1, fp );
- fread( &Overall_agitation, sizeof(int), 1, fp );
- fread( Ai_local_info, sizeof(ai_local)*MAX_OBJECTS, 1, fp );
- fread( Point_segs, sizeof(point_seg)*MAX_POINT_SEGS, 1, fp );
- fread( Ai_cloak_info, sizeof(ai_cloak_info)*MAX_AI_CLOAK_INFO, 1, fp );
- fread( &Boss_cloak_start_time, sizeof(fix), 1, fp );
- fread( &Boss_cloak_end_time , sizeof(fix), 1, fp );
- fread( &Last_teleport_time , sizeof(fix), 1, fp );
- fread( &Boss_teleport_interval, sizeof(fix), 1, fp );
- fread( &Boss_cloak_interval, sizeof(fix), 1, fp );
- fread( &Boss_cloak_duration, sizeof(fix), 1, fp );
- fread( &Last_gate_time, sizeof(fix), 1, fp );
- fread( &Gate_interval, sizeof(fix), 1, fp );
- fread( &Boss_dying_start_time, sizeof(fix), 1, fp );
- fread( &Boss_dying, sizeof(int), 1, fp );
- fread( &Boss_dying_sound_playing, sizeof(int), 1, fp );
- fread( &Boss_hit_this_frame, sizeof(int), 1, fp );
- fread( &Boss_been_hit, sizeof(int), 1, fp );
- return 1;
- }
- // -- void show_path_and_other(object *objp )
- // -- {
- // -- int i;
- // -- ai_static *aip = &objp->ctype.ai_info;
- // -- for (i=0; i<aip->path_length; i++)
- // -- mprintf((0, "%2i ", Point_segs[aip->hide_index+i].segnum));
- // -- mprintf((0, "[pl: %i cur: st: %i]\n", ConsoleObject->segnum, objp->segnum, aip->hide_segment));
- // -- }
|