Soldier Control.c 293 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158
  1. #ifdef PRECOMPILEDHEADERS
  2. #include "Tactical All.h"
  3. #else
  4. #include <wchar.h>
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include "wcheck.h"
  8. #include "Render Fun.h"
  9. #include "stdlib.h"
  10. #include "debug.h"
  11. #include "MemMan.h"
  12. #include "Overhead Types.h"
  13. #include "Soldier Control.h"
  14. #include "Animation Cache.h"
  15. #include "Animation Data.h"
  16. #include "Animation Control.h"
  17. #include "container.h"
  18. #include <math.h>
  19. #include "pathai.h"
  20. #include "Random.h"
  21. #include "worldman.h"
  22. #include "Isometric Utils.h"
  23. #include "Render Dirty.h"
  24. #include "renderworld.h"
  25. #include "sys globals.h"
  26. #include "video.h"
  27. #include "points.h"
  28. #include "Win util.h"
  29. #include "Sound Control.h"
  30. #include "weapons.h"
  31. #include "vobject_blitters.h"
  32. #include "Handle UI.h"
  33. #include "soldier ani.h"
  34. #include "Event pump.h"
  35. #include "opplist.h"
  36. #include "ai.h"
  37. #include "interface.h"
  38. #include "lighting.h"
  39. #include "faces.h"
  40. #include "Soldier Profile.h"
  41. #include "gap.h"
  42. #include "interface panels.h"
  43. #include "campaign.h"
  44. #include "soldier macros.h"
  45. #include "english.h"
  46. #include "Squads.h"
  47. #ifdef NETWORKED
  48. #include "Networking.h"
  49. #include "NetworkEvent.h"
  50. #endif
  51. #include "structure wrap.h"
  52. #include "items.h"
  53. #include "Soundman.h"
  54. #include "utilities.h"
  55. #include "Strategic.h"
  56. #include "soldier tile.h"
  57. #include "Smell.h"
  58. #include "Keys.h"
  59. #include "dialogue control.h"
  60. #include "soldier functions.h"
  61. #include "rt time defines.h"
  62. #include "Exit Grids.h"
  63. #include "gamescreen.h"
  64. #include "Quests.h"
  65. #include "message.h"
  66. #include "NPC.h"
  67. #include "SkillCheck.h"
  68. #include "handle doors.h"
  69. #include "interface dialogue.h"
  70. #include "smokeeffects.h"
  71. #include "GameSettings.h"
  72. #include "tile animation.h"
  73. #include "ShopKeeper Interface.h"
  74. #include "Arms Dealer Init.h"
  75. #include "vehicles.h"
  76. #include "rotting corpses.h"
  77. #include "Interface Control.h"
  78. #include "strategicmap.h"
  79. #include "morale.h"
  80. #include "meanwhile.h"
  81. #include "drugs and alcohol.h"
  82. #include "SkillCheck.h"
  83. #include "boxing.h"
  84. #include "overhead map.h"
  85. #endif
  86. extern INT16 DirIncrementer[8];
  87. #define PALETTEFILENAME "BINARYDATA\\ja2pal.dat"
  88. #define LOW_MORALE_BATTLE_SND_THREASHOLD 35
  89. #define TURNING_FROM_PRONE_OFF 0
  90. #define TURNING_FROM_PRONE_ON 1
  91. #define TURNING_FROM_PRONE_START_UP_FROM_MOVE 2
  92. #define TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE 3
  93. #define MIN_SUBSEQUENT_SNDS_DELAY 2000
  94. // Enumerate extended directions
  95. enum
  96. {
  97. EX_NORTH = 0,
  98. EX_NORTHEAST = 4,
  99. EX_EAST = 8,
  100. EX_SOUTHEAST = 12,
  101. EX_SOUTH = 16,
  102. EX_SOUTHWEST = 20,
  103. EX_WEST = 24,
  104. EX_NORTHWEST = 28,
  105. EX_NUM_WORLD_DIRECTIONS = 32,
  106. EX_DIRECTION_IRRELEVANT
  107. } ExtendedWorldDirections;
  108. // LUT for conversion from 8-direction to extended direction
  109. UINT8 ubExtDirection[] =
  110. {
  111. EX_NORTH,
  112. EX_NORTHEAST,
  113. EX_EAST,
  114. EX_SOUTHEAST,
  115. EX_SOUTH,
  116. EX_SOUTHWEST,
  117. EX_WEST,
  118. EX_NORTHWEST
  119. };
  120. UINT8 gExtOneCDirection[ EX_NUM_WORLD_DIRECTIONS ] =
  121. {
  122. 4,
  123. 5,
  124. 6,
  125. 7,
  126. 8,
  127. 9,
  128. 10,
  129. 11,
  130. 12,
  131. 13,
  132. 14,
  133. 15,
  134. 16,
  135. 17,
  136. 18,
  137. 19,
  138. 20,
  139. 21,
  140. 22,
  141. 23,
  142. 24,
  143. 25,
  144. 26,
  145. 27,
  146. 28,
  147. 29,
  148. 30,
  149. 31,
  150. 0,
  151. 1,
  152. 2,
  153. 3,
  154. };
  155. typedef struct
  156. {
  157. CHAR8 zName[20];
  158. UINT8 ubRandomVal;
  159. BOOLEAN fPreload;
  160. BOOLEAN fBadGuy;
  161. BOOLEAN fDontAllowTwoInRow;
  162. BOOLEAN fStopDialogue;
  163. } BATTLESNDS_STRUCT;
  164. BATTLESNDS_STRUCT gBattleSndsData[] =
  165. {
  166. "ok1", 2, 1, 1, 1, 2,
  167. "ok2", 0, 1, 1, 1, 2,
  168. "cool", 0, 1, 0, 1, 0,
  169. "curse", 0, 1, 1, 1, 0,
  170. "hit1", 2, 1, 1, 1, 1,
  171. "hit2", 0, 1, 1, 1, 1,
  172. "laugh", 0, 1, 1, 1, 0,
  173. "attn", 0, 1, 0, 1, 0,
  174. "dying", 0, 1, 1, 1, 1,
  175. "humm", 0, 0, 0, 1, 1,
  176. "noth", 0, 0, 0, 1, 1,
  177. "gotit", 0, 0, 0, 1, 1,
  178. "lmok1", 2, 1, 0, 1, 2,
  179. "lmok2", 0, 1, 0, 1, 2,
  180. "lmattn", 0, 1, 0, 1, 0,
  181. "locked", 0, 0, 0, 1, 0,
  182. "enem", 0, 1, 1, 1, 0,
  183. };
  184. BOOLEAN IsValidSecondHandShot( SOLDIERTYPE *pSoldier );
  185. UINT8 bHealthStrRanges[] =
  186. {
  187. 15,
  188. 30,
  189. 45,
  190. 60,
  191. 75,
  192. 90,
  193. 101
  194. };
  195. INT16 gsTerrainTypeSpeedModifiers[] =
  196. {
  197. 5, // Flat ground
  198. 5, // Floor
  199. 5, // Paved road
  200. 5, // Dirt road
  201. 10, // LOW GRASS
  202. 15, // HIGH GRASS
  203. 20, // TRAIN TRACKS
  204. 20, // LOW WATER
  205. 25, // MID WATER
  206. 30 // DEEP WATER
  207. };
  208. //Kris:
  209. //Temporary for testing the speed of the translucency. Pressing Ctrl+L in turn based
  210. //input will toggle this flag. When clear, the translucency checking is turned off to
  211. //increase the speed of the game.
  212. BOOLEAN gfCalcTranslucency = FALSE;
  213. INT16 gsFullTileDirections[ MAX_FULLTILE_DIRECTIONS ] =
  214. {
  215. -1, -WORLD_COLS - 1, -WORLD_COLS
  216. };
  217. // Palette ranges
  218. UINT32 guiNumPaletteSubRanges;
  219. PaletteSubRangeType *guipPaletteSubRanges = NULL;
  220. // Palette replacements
  221. UINT32 guiNumReplacements;
  222. PaletteReplacementType *guipPaletteReplacements = NULL;
  223. extern BOOLEAN fReDrawFace;
  224. extern UINT8 gubWaitingForAllMercsToExitCode;
  225. BOOLEAN gfGetNewPathThroughPeople = FALSE;
  226. // LOCAL FUNCTIONS
  227. // DO NOT CALL UNLESS THROUGH EVENT_SetSoldierPosition
  228. UINT16 PickSoldierReadyAnimation( SOLDIERTYPE *pSoldier, BOOLEAN fEndReady );
  229. BOOLEAN CheckForFullStruct( INT16 sGridNo, UINT16 *pusIndex );
  230. void SetSoldierLocatorOffsets( SOLDIERTYPE *pSoldier );
  231. void CheckForFullStructures( SOLDIERTYPE *pSoldier );
  232. BOOLEAN InitNewSoldierState( SOLDIERTYPE *pSoldier, UINT8 ubNewState, UINT16 usStartingAniCode );
  233. UINT16 GetNewSoldierStateFromNewStance( SOLDIERTYPE *pSoldier, UINT8 ubDesiredStance );
  234. void SetSoldierAniSpeed( SOLDIERTYPE *pSoldier );
  235. void AdjustForFastTurnAnimation( SOLDIERTYPE *pSoldier );
  236. UINT16 SelectFireAnimation( SOLDIERTYPE *pSoldier, UINT8 ubHeight );
  237. void SelectFallAnimation( SOLDIERTYPE *pSoldier );
  238. BOOLEAN FullStructAlone( INT16 sGridNo, UINT8 ubRadius );
  239. void SoldierGotHitGunFire( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial , UINT8 ubHitLocation);
  240. void SoldierGotHitBlade( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation );
  241. void SoldierGotHitPunch( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation );
  242. void SoldierGotHitExplosion( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation );
  243. UINT8 CalcScreamVolume( SOLDIERTYPE * pSoldier, UINT8 ubCombinedLoss );
  244. void PlaySoldierFootstepSound( SOLDIERTYPE *pSoldier );
  245. void HandleSystemNewAISituation( SOLDIERTYPE *pSoldier, BOOLEAN fResetABC );
  246. UINT16 *CreateEnemyGlow16BPPPalette( SGPPaletteEntry *pPalette, UINT32 rscale, UINT32 gscale, BOOLEAN fAdjustGreen );
  247. UINT16 *CreateEnemyGreyGlow16BPPPalette( SGPPaletteEntry *pPalette, UINT32 rscale, UINT32 gscale, BOOLEAN fAdjustGreen );
  248. void SoldierBleed( SOLDIERTYPE *pSoldier, BOOLEAN fBandagedBleed );
  249. INT32 CheckBleeding( SOLDIERTYPE *pSoldier );
  250. void EVENT_InternalSetSoldierDesiredDirection( SOLDIERTYPE *pSoldier, UINT16 usNewDirection, BOOLEAN fInitalMove, UINT16 usAnimState );
  251. #ifdef JA2BETAVERSION
  252. extern void ValidatePlayersAreInOneGroupOnly();
  253. extern void MapScreenDefaultOkBoxCallback( UINT8 bExitValue );
  254. void SAIReportError( STR16 wErrorString );
  255. #endif
  256. UINT32 SleepDartSuccumbChance( SOLDIERTYPE * pSoldier );
  257. void EnableDisableSoldierLightEffects( BOOLEAN fEnableLights );
  258. void SetSoldierPersonalLightLevel( SOLDIERTYPE *pSoldier );
  259. void HandleVehicleMovementSound( SOLDIERTYPE *pSoldier, BOOLEAN fOn )
  260. {
  261. VEHICLETYPE *pVehicle = &( pVehicleList[ pSoldier->bVehicleID ] );
  262. if ( fOn )
  263. {
  264. if ( pVehicle->iMovementSoundID == NO_SAMPLE )
  265. {
  266. pVehicle->iMovementSoundID = PlayJA2Sample( pVehicle->iMoveSound, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
  267. }
  268. }
  269. else
  270. {
  271. if ( pVehicle->iMovementSoundID != NO_SAMPLE )
  272. {
  273. SoundStop( pVehicle->iMovementSoundID );
  274. pVehicle->iMovementSoundID = NO_SAMPLE;
  275. }
  276. }
  277. }
  278. void AdjustNoAPToFinishMove( SOLDIERTYPE *pSoldier, BOOLEAN fSet )
  279. {
  280. if ( pSoldier->ubBodyType == CROW )
  281. {
  282. return;
  283. }
  284. // Check if we are a vehicle first....
  285. if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
  286. {
  287. // Turn off sound effects....
  288. if ( fSet )
  289. {
  290. HandleVehicleMovementSound( pSoldier, FALSE );
  291. }
  292. }
  293. // Turn off sound effects....
  294. if ( fSet )
  295. {
  296. // Position light....
  297. // SetCheckSoldierLightFlag( pSoldier );
  298. }
  299. else
  300. {
  301. // DeleteSoldierLight( pSoldier );
  302. }
  303. pSoldier->fNoAPToFinishMove = fSet;
  304. if ( !fSet )
  305. {
  306. // return reason to default value
  307. pSoldier->ubReasonCantFinishMove = REASON_STOPPED_NO_APS;
  308. }
  309. }
  310. void HandleCrowShadowVisibility( SOLDIERTYPE *pSoldier )
  311. {
  312. if ( pSoldier->ubBodyType == CROW )
  313. {
  314. if ( pSoldier->usAnimState == CROW_FLY )
  315. {
  316. if ( pSoldier->pAniTile != NULL )
  317. {
  318. if ( pSoldier->bLastRenderVisibleValue != -1 )
  319. {
  320. HideAniTile( pSoldier->pAniTile, FALSE );
  321. }
  322. else
  323. {
  324. HideAniTile( pSoldier->pAniTile, TRUE );
  325. }
  326. }
  327. }
  328. }
  329. }
  330. void HandleCrowShadowNewGridNo( SOLDIERTYPE *pSoldier )
  331. {
  332. ANITILE_PARAMS AniParams;
  333. memset( &AniParams, 0, sizeof( ANITILE_PARAMS ) );
  334. if ( pSoldier->ubBodyType == CROW )
  335. {
  336. if ( pSoldier->pAniTile != NULL )
  337. {
  338. DeleteAniTile( pSoldier->pAniTile );
  339. pSoldier->pAniTile = NULL;
  340. }
  341. if ( pSoldier->sGridNo != NOWHERE )
  342. {
  343. if ( pSoldier->usAnimState == CROW_FLY )
  344. {
  345. AniParams.sGridNo = (INT16)pSoldier->sGridNo;
  346. AniParams.ubLevelID = ANI_SHADOW_LEVEL;
  347. AniParams.sDelay = pSoldier->sAniDelay;
  348. AniParams.sStartFrame = 0;
  349. AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_LOOPING | ANITILE_USE_DIRECTION_FOR_START_FRAME;
  350. AniParams.sX = pSoldier->sX;
  351. AniParams.sY = pSoldier->sY;
  352. AniParams.sZ = 0;
  353. strcpy( AniParams.zCachedFile, "TILECACHE\\FLY_SHDW.STI" );
  354. AniParams.uiUserData3 = pSoldier->bDirection;
  355. pSoldier->pAniTile = CreateAnimationTile( &AniParams );
  356. HandleCrowShadowVisibility( pSoldier );
  357. }
  358. }
  359. }
  360. }
  361. void HandleCrowShadowRemoveGridNo( SOLDIERTYPE *pSoldier )
  362. {
  363. if ( pSoldier->ubBodyType == CROW )
  364. {
  365. if ( pSoldier->usAnimState == CROW_FLY )
  366. {
  367. if ( pSoldier->pAniTile != NULL )
  368. {
  369. DeleteAniTile( pSoldier->pAniTile );
  370. pSoldier->pAniTile = NULL;
  371. }
  372. }
  373. }
  374. }
  375. void HandleCrowShadowNewDirection( SOLDIERTYPE *pSoldier )
  376. {
  377. if ( pSoldier->ubBodyType == CROW )
  378. {
  379. if ( pSoldier->usAnimState == CROW_FLY )
  380. {
  381. if ( pSoldier->pAniTile != NULL )
  382. {
  383. pSoldier->pAniTile->uiUserData3 = pSoldier->bDirection;
  384. }
  385. }
  386. }
  387. }
  388. void HandleCrowShadowNewPosition( SOLDIERTYPE *pSoldier )
  389. {
  390. if ( pSoldier->ubBodyType == CROW )
  391. {
  392. if ( pSoldier->usAnimState == CROW_FLY )
  393. {
  394. if ( pSoldier->pAniTile != NULL )
  395. {
  396. pSoldier->pAniTile->sRelativeX = pSoldier->sX;
  397. pSoldier->pAniTile->sRelativeY = pSoldier->sY;
  398. }
  399. }
  400. }
  401. }
  402. INT8 CalcActionPoints(SOLDIERTYPE *pSold)
  403. {
  404. UINT8 ubPoints,ubMaxAPs;
  405. INT8 bBandage;
  406. // dead guys don't get any APs (they shouldn't be here asking for them!)
  407. if (!pSold->bLife)
  408. return(0);
  409. // people with sleep dart drug who have collapsed get no APs
  410. if ( (pSold->bSleepDrugCounter > 0) && pSold->bCollapsed )
  411. return( 0 );
  412. // Calculate merc's action points at 100% capability (range is 10 - 25)
  413. // round fractions of .5 up (that's why the +20 before the division!
  414. ubPoints = 5 + (((10 * EffectiveExpLevel( pSold ) +
  415. 3 * EffectiveAgility( pSold ) +
  416. 2 * pSold->bLifeMax +
  417. 2 * EffectiveDexterity( pSold ) ) + 20) / 40);
  418. //if (GameOption[INCREASEDAP] % 2 == 1)
  419. //points += AP_INCREASE;
  420. // Calculate bandage
  421. bBandage = pSold->bLifeMax - pSold->bLife - pSold->bBleeding;
  422. // If injured, reduce action points accordingly (by up to 2/3rds)
  423. if (pSold->bLife < pSold->bLifeMax)
  424. {
  425. ubPoints -= (2 * ubPoints * (pSold->bLifeMax - pSold->bLife + (bBandage / 2))) /
  426. (3 * pSold->bLifeMax);
  427. }
  428. // If tired, reduce action points accordingly (by up to 1/2)
  429. if (pSold->bBreath < 100)
  430. ubPoints -= (ubPoints * (100 - pSold->bBreath)) / 200;
  431. if (pSold->sWeightCarriedAtTurnStart > 100)
  432. {
  433. ubPoints = (UINT8) ( ((UINT32)ubPoints) * 100 / pSold->sWeightCarriedAtTurnStart );
  434. }
  435. // If resulting APs are below our permitted minimum, raise them to it!
  436. if (ubPoints < AP_MINIMUM)
  437. ubPoints = AP_MINIMUM;
  438. // make sure action points doesn't exceed the permitted maximum
  439. ubMaxAPs = gubMaxActionPoints[ pSold->ubBodyType ];
  440. //if (GameOption[INCREASEDAP] % 2 == 1)
  441. // maxAPs += AP_INCREASE;
  442. // If resulting APs are below our permitted minimum, raise them to it!
  443. if (ubPoints > ubMaxAPs)
  444. ubPoints = ubMaxAPs;
  445. if ( pSold->ubBodyType == BLOODCAT )
  446. {
  447. // use same as young monsters
  448. ubPoints = (ubPoints * AP_YOUNG_MONST_FACTOR) / 10;
  449. }
  450. else if (pSold->uiStatusFlags & SOLDIER_MONSTER)
  451. {
  452. // young monsters get extra APs
  453. if ( pSold->ubBodyType == YAF_MONSTER || pSold->ubBodyType == YAM_MONSTER || pSold->ubBodyType == INFANT_MONSTER )
  454. {
  455. ubPoints = (ubPoints * AP_YOUNG_MONST_FACTOR) / 10;
  456. }
  457. // if frenzied, female monsters get more APs! (for young females, cumulative!)
  458. if (pSold->bFrenzied)
  459. {
  460. ubPoints = (ubPoints * AP_MONST_FRENZY_FACTOR) / 10;
  461. }
  462. }
  463. // adjust APs for phobia situations
  464. if ( pSold->ubProfile != NO_PROFILE )
  465. {
  466. if ( (gMercProfiles[ pSold->ubProfile ].bPersonalityTrait == CLAUSTROPHOBIC) && (gbWorldSectorZ > 0) )
  467. {
  468. ubPoints = (ubPoints * AP_CLAUSTROPHOBE) / 10;
  469. }
  470. else if ( (gMercProfiles[ pSold->ubProfile ].bPersonalityTrait == FEAR_OF_INSECTS) && (MercSeesCreature( pSold ) ) )
  471. {
  472. ubPoints = (ubPoints * AP_AFRAID_OF_INSECTS) / 10;
  473. }
  474. }
  475. // Adjusat APs due to drugs...
  476. HandleAPEffectDueToDrugs( pSold, &ubPoints );
  477. // If we are a vehicle, adjust APS...
  478. if ( pSold->uiStatusFlags & SOLDIER_VEHICLE )
  479. {
  480. AdjustVehicleAPs( pSold, &ubPoints );
  481. }
  482. // if we are in boxing mode, adjust APs... THIS MUST BE LAST!
  483. if ( gTacticalStatus.bBoxingState == BOXING || gTacticalStatus.bBoxingState == PRE_BOXING )
  484. {
  485. ubPoints /= 2;
  486. }
  487. return (ubPoints);
  488. }
  489. void CalcNewActionPoints( SOLDIERTYPE *pSoldier )
  490. {
  491. if ( gTacticalStatus.bBoxingState == BOXING || gTacticalStatus.bBoxingState == PRE_BOXING )
  492. {
  493. // if we are in boxing mode, carry 1/2 as many points
  494. if (pSoldier->bActionPoints > MAX_AP_CARRIED / 2)
  495. {
  496. pSoldier->bActionPoints = MAX_AP_CARRIED / 2;
  497. }
  498. }
  499. else
  500. {
  501. if (pSoldier->bActionPoints > MAX_AP_CARRIED)
  502. {
  503. pSoldier->bActionPoints = MAX_AP_CARRIED;
  504. }
  505. }
  506. pSoldier->bActionPoints += CalcActionPoints( pSoldier);
  507. // Don't max out if we are drugged....
  508. if ( !GetDrugEffect( pSoldier, DRUG_TYPE_ADRENALINE ) )
  509. {
  510. pSoldier->bActionPoints = __min( pSoldier->bActionPoints, gubMaxActionPoints[ pSoldier->ubBodyType ] );
  511. }
  512. pSoldier->bInitialActionPoints = pSoldier->bActionPoints;
  513. }
  514. void DoNinjaAttack( SOLDIERTYPE *pSoldier )
  515. {
  516. //UINT32 uiMercFlags;
  517. UINT16 usSoldierIndex;
  518. SOLDIERTYPE *pTSoldier;
  519. UINT8 ubTDirection;
  520. UINT8 ubTargetStance;
  521. usSoldierIndex = WhoIsThere2( pSoldier->sTargetGridNo, pSoldier->bLevel );
  522. if ( usSoldierIndex != NOBODY )
  523. {
  524. GetSoldier( &pTSoldier, usSoldierIndex );
  525. // Look at stance of target
  526. ubTargetStance = gAnimControl[ pTSoldier->usAnimState ].ubEndHeight;
  527. // Get his life...if < certain value, do finish!
  528. if ( (pTSoldier->bLife <= 30 || pTSoldier->bBreath <= 30) && ubTargetStance != ANIM_PRONE )
  529. {
  530. // Do finish!
  531. ChangeSoldierState( pSoldier, NINJA_SPINKICK, 0 , FALSE );
  532. }
  533. else
  534. {
  535. if ( ubTargetStance != ANIM_PRONE )
  536. {
  537. if ( Random( 2 ) == 0 )
  538. {
  539. ChangeSoldierState( pSoldier, NINJA_LOWKICK, 0 , FALSE );
  540. }
  541. else
  542. {
  543. ChangeSoldierState( pSoldier, NINJA_PUNCH, 0 , FALSE );
  544. }
  545. // CHECK IF HE CAN SEE US, IF SO CHANGE DIRECTION
  546. if ( pTSoldier->bOppList[ pSoldier->ubID ] == 0 && pTSoldier->bTeam != pSoldier->bTeam )
  547. {
  548. if ( !( pTSoldier->uiStatusFlags & ( SOLDIER_MONSTER | SOLDIER_ANIMAL | SOLDIER_VEHICLE ) ) )
  549. {
  550. ubTDirection = (UINT8)GetDirectionFromGridNo( pSoldier->sGridNo, pTSoldier );
  551. SendSoldierSetDesiredDirectionEvent( pTSoldier, ubTDirection );
  552. }
  553. }
  554. }
  555. else
  556. {
  557. // CHECK OUR STANCE
  558. if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_CROUCH )
  559. {
  560. // SET DESIRED STANCE AND SET PENDING ANIMATION
  561. SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH );
  562. pSoldier->usPendingAnimation = PUNCH_LOW;
  563. }
  564. else
  565. {
  566. // USE crouched one
  567. // NEED TO CHANGE STANCE IF NOT CROUCHD!
  568. EVENT_InitNewSoldierAnim( pSoldier, PUNCH_LOW, 0 , FALSE );
  569. }
  570. }
  571. }
  572. }
  573. if ( pSoldier->ubProfile == 33 )
  574. {
  575. UINT32 uiSoundID;
  576. SOUNDPARMS spParms;
  577. INT32 iFaceIndex;
  578. // Play sound!
  579. memset(&spParms, 0xff, sizeof(SOUNDPARMS));
  580. spParms.uiSpeed = RATE_11025;
  581. spParms.uiVolume = (INT8)CalculateSpeechVolume( HIGHVOLUME );
  582. // If we are an enemy.....reduce due to volume
  583. if ( pSoldier->bTeam != gbPlayerNum )
  584. {
  585. spParms.uiVolume = SoundVolume( (UINT8)spParms.uiVolume, pSoldier->sGridNo );
  586. }
  587. spParms.uiLoop = 1;
  588. spParms.uiPan = SoundDir( pSoldier->sGridNo );
  589. spParms.uiPriority=GROUP_PLAYER;
  590. if ( pSoldier->usAnimState == NINJA_SPINKICK )
  591. {
  592. uiSoundID = SoundPlay( "BATTLESNDS\\033_CHOP2.WAV", &spParms );
  593. }
  594. else
  595. {
  596. if ( Random( 2 ) == 0 )
  597. {
  598. uiSoundID = SoundPlay( "BATTLESNDS\\033_CHOP3.WAV", &spParms );
  599. }
  600. else
  601. {
  602. uiSoundID = SoundPlay( "BATTLESNDS\\033_CHOP1.WAV", &spParms );
  603. }
  604. }
  605. if ( uiSoundID != SOUND_ERROR )
  606. {
  607. pSoldier->uiBattleSoundID = uiSoundID;
  608. if ( pSoldier->ubProfile != NO_PROFILE )
  609. {
  610. // Get soldier's face ID
  611. iFaceIndex = pSoldier->iFaceIndex;
  612. // Check face index
  613. if ( iFaceIndex != -1 )
  614. {
  615. ExternSetFaceTalking( iFaceIndex, uiSoundID );
  616. }
  617. }
  618. }
  619. }
  620. }
  621. BOOLEAN CreateSoldierCommon( UINT8 ubBodyType, SOLDIERTYPE *pSoldier, UINT16 usSoldierID, UINT16 usState )
  622. {
  623. BOOLEAN fSuccess = FALSE;
  624. INT32 iCounter = 0;
  625. //if we are loading a saved game, we DO NOT want to reset the opplist, look for enemies, or say a dying commnet
  626. if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
  627. {
  628. // Set initial values for opplist!
  629. InitSoldierOppList( pSoldier );
  630. HandleSight( pSoldier, SIGHT_LOOK );
  631. // Set some quote flags
  632. if ( pSoldier->bLife >= OKLIFE )
  633. {
  634. pSoldier->fDyingComment = FALSE;
  635. }
  636. else
  637. {
  638. pSoldier->fDyingComment = TRUE;
  639. }
  640. }
  641. // ATE: Reset some timer flags...
  642. pSoldier->uiTimeSameBattleSndDone = 0;
  643. // ATE: Reset every time.....
  644. pSoldier->fSoldierWasMoving = TRUE;
  645. pSoldier->iTuringSoundID = NO_SAMPLE;
  646. pSoldier->uiTimeSinceLastBleedGrunt = 0;
  647. if ( pSoldier->ubBodyType == QUEENMONSTER )
  648. {
  649. pSoldier->iPositionSndID = NewPositionSnd( NOWHERE, POSITION_SOUND_FROM_SOLDIER, (UINT32)pSoldier, QUEEN_AMBIENT_NOISE );
  650. }
  651. // ANYTHING AFTER HERE CAN FAIL
  652. do
  653. {
  654. if (usSoldierID <= gTacticalStatus.Team[ OUR_TEAM ].bLastID)
  655. {
  656. pSoldier->pKeyRing = MemAlloc( NUM_KEYS * sizeof( KEY_ON_RING ) );
  657. memset( pSoldier->pKeyRing , 0, NUM_KEYS * sizeof( KEY_ON_RING ) );
  658. for( iCounter = 0; iCounter < NUM_KEYS; iCounter++ )
  659. {
  660. pSoldier->pKeyRing[ iCounter ].ubKeyID = INVALID_KEY_NUMBER;
  661. }
  662. }
  663. else
  664. {
  665. pSoldier->pKeyRing = NULL;
  666. }
  667. // Create frame cache
  668. if( InitAnimationCache( usSoldierID, &( pSoldier->AnimCache) ) == FALSE )
  669. {
  670. DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed animation cache creation" ) );
  671. break;
  672. }
  673. if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
  674. {
  675. // Init new soldier state
  676. // OFFSET FIRST ANIMATION FRAME FOR NEW MERCS
  677. if ( usState != STANDING )
  678. {
  679. EVENT_InitNewSoldierAnim( pSoldier, usState, (UINT8)0, TRUE );
  680. }
  681. else
  682. {
  683. EVENT_InitNewSoldierAnim( pSoldier, usState, (UINT8)Random( 10 ), TRUE );
  684. }
  685. }
  686. else
  687. {
  688. /// if we don't have a world loaded, and are in a bad anim, goto standing.
  689. // bad anims are: HOPFENCE,
  690. // CLIMBDOWNROOF, FALLFORWARD_ROOF,FALLOFF, CLIMBUPROOF
  691. if( !gfWorldLoaded &&
  692. ( usState == HOPFENCE ||
  693. usState == CLIMBDOWNROOF ||
  694. usState == FALLFORWARD_ROOF ||
  695. usState == FALLOFF ||
  696. usState == CLIMBUPROOF ) )
  697. {
  698. EVENT_InitNewSoldierAnim( pSoldier, STANDING, 0, TRUE );
  699. }
  700. else
  701. {
  702. EVENT_InitNewSoldierAnim( pSoldier, usState, pSoldier->usAniCode, TRUE );
  703. }
  704. }
  705. //if ( pSoldier->pBackGround != NULL )
  706. // MemFree( pSoldier->pBackGround );
  707. // INIT ANIMATION DATA
  708. //if((pSoldier->pBackGround=MemAlloc(SOLDIER_UNBLIT_SIZE))==NULL)
  709. //{
  710. // DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed unblit memory allocation" ) );
  711. // break;
  712. //}
  713. //memset(pSoldier->pBackGround, 0, SOLDIER_UNBLIT_SIZE);
  714. //if((pSoldier->pZBackground=MemAlloc(SOLDIER_UNBLIT_SIZE))==NULL)
  715. //{
  716. // DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed unblit memory allocation" ) );
  717. // break;
  718. //}
  719. //memset(pSoldier->pZBackground, 0, SOLDIER_UNBLIT_SIZE);
  720. // Init palettes
  721. if( CreateSoldierPalettes( pSoldier ) == FALSE )
  722. {
  723. DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed in creating soldier palettes" ) );
  724. break;
  725. }
  726. fSuccess = TRUE;
  727. } while( FALSE );
  728. if ( !fSuccess )
  729. {
  730. DeleteSoldier( (pSoldier ) );
  731. }
  732. return( fSuccess );
  733. }
  734. BOOLEAN DeleteSoldier( SOLDIERTYPE *pSoldier )
  735. {
  736. UINT32 cnt;
  737. INT32 iGridNo;
  738. INT8 bDir;
  739. BOOLEAN fRet;
  740. if ( pSoldier != NULL )
  741. {
  742. //if(pSoldier->pBackGround!=NULL)
  743. //MemFree(pSoldier->pBackGround);
  744. //if(pSoldier->pZBackground!=NULL)
  745. //MemFree(pSoldier->pZBackground);
  746. if( pSoldier->sGridNo != NOWHERE )
  747. {
  748. // Remove adjacency records
  749. for (bDir = 0; bDir < NUM_WORLD_DIRECTIONS; bDir++)
  750. {
  751. iGridNo = pSoldier->sGridNo + DirIncrementer[ bDir ];
  752. if( iGridNo >= 0 && iGridNo < WORLD_MAX )
  753. {
  754. gpWorldLevelData[ iGridNo ].ubAdjacentSoldierCnt--;
  755. }
  756. }
  757. }
  758. // Delete key ring
  759. if (pSoldier->pKeyRing)
  760. {
  761. MemFree( pSoldier->pKeyRing );
  762. pSoldier->pKeyRing = NULL;
  763. }
  764. // Delete faces
  765. DeleteSoldierFace( pSoldier );
  766. // FREE PALETTES
  767. if ( pSoldier->p8BPPPalette != NULL )
  768. {
  769. MemFree( pSoldier->p8BPPPalette );
  770. pSoldier->p8BPPPalette = NULL;
  771. }
  772. if ( pSoldier->p16BPPPalette != NULL )
  773. {
  774. MemFree( pSoldier->p16BPPPalette );
  775. pSoldier->p16BPPPalette = NULL;
  776. }
  777. for ( cnt = 0; cnt < NUM_SOLDIER_SHADES; cnt++ )
  778. {
  779. if ( pSoldier->pShades[ cnt ] != NULL )
  780. {
  781. MemFree( pSoldier->pShades[ cnt ] );
  782. pSoldier->pShades[ cnt ] = NULL;
  783. }
  784. }
  785. for ( cnt = 0; cnt < NUM_SOLDIER_EFFECTSHADES; cnt++ )
  786. {
  787. if ( pSoldier->pEffectShades[ cnt ] != NULL )
  788. {
  789. MemFree( pSoldier->pEffectShades[ cnt ] );
  790. pSoldier->pEffectShades[ cnt ] = NULL;
  791. }
  792. }
  793. // Delete glows
  794. for ( cnt = 0; cnt < 20; cnt++ )
  795. {
  796. if ( pSoldier->pGlowShades[ cnt ] != NULL )
  797. {
  798. MemFree( pSoldier->pGlowShades[ cnt ] );
  799. pSoldier->pGlowShades[ cnt ] = NULL;
  800. }
  801. }
  802. if ( pSoldier->ubBodyType == QUEENMONSTER )
  803. {
  804. DeletePositionSnd( pSoldier->iPositionSndID );
  805. }
  806. // Free any animations we may have locked...
  807. UnLoadCachedAnimationSurfaces( pSoldier->ubID, &( pSoldier->AnimCache) );
  808. // Free Animation cache
  809. DeleteAnimationCache( pSoldier->ubID, &( pSoldier->AnimCache) );
  810. // Soldier is not active
  811. pSoldier->bActive = FALSE;
  812. // Remove light
  813. DeleteSoldierLight( pSoldier );
  814. // Remove reseved movement value
  815. UnMarkMovementReserved( pSoldier );
  816. }
  817. // REMOVE SOLDIER FROM SLOT!
  818. fRet = RemoveMercSlot( pSoldier );
  819. if (!fRet)
  820. {
  821. RemoveAwaySlot( pSoldier );
  822. }
  823. return( TRUE );
  824. }
  825. BOOLEAN CreateSoldierLight( SOLDIERTYPE *pSoldier )
  826. {
  827. if ( pSoldier->bTeam != gbPlayerNum )
  828. {
  829. return( FALSE );
  830. }
  831. // DO ONLY IF WE'RE AT A GOOD LEVEL
  832. if ( pSoldier->iLight == -1 )
  833. {
  834. // ATE: Check for goggles in headpos....
  835. if ( pSoldier->inv[ HEAD1POS ].usItem == NIGHTGOGGLES ||
  836. pSoldier->inv[ HEAD2POS ].usItem == NIGHTGOGGLES )
  837. {
  838. if( ( pSoldier->iLight=LightSpriteCreate("Light3", 0 ) )==(-1))
  839. {
  840. DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed loading light" ) );
  841. return( FALSE );
  842. }
  843. else
  844. {
  845. LightSprites[ pSoldier->iLight ].uiFlags |= MERC_LIGHT;
  846. }
  847. }
  848. else if ( pSoldier->inv[ HEAD1POS ].usItem == UVGOGGLES ||
  849. pSoldier->inv[ HEAD2POS ].usItem == UVGOGGLES )
  850. {
  851. if( ( pSoldier->iLight=LightSpriteCreate("Light4", 0 ) )==(-1))
  852. {
  853. DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed loading light" ) );
  854. return( FALSE );
  855. }
  856. else
  857. {
  858. LightSprites[ pSoldier->iLight ].uiFlags |= MERC_LIGHT;
  859. }
  860. }
  861. else
  862. {
  863. if( ( pSoldier->iLight=LightSpriteCreate("Light2", 0 ) )==(-1))
  864. {
  865. DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed loading light" ) );
  866. return( FALSE );
  867. }
  868. else
  869. {
  870. LightSprites[ pSoldier->iLight ].uiFlags |= MERC_LIGHT;
  871. }
  872. }
  873. if ( pSoldier->bLevel != 0 )
  874. {
  875. LightSpriteRoofStatus(pSoldier->iLight, TRUE );
  876. }
  877. }
  878. return( TRUE );
  879. }
  880. BOOLEAN ReCreateSoldierLight( SOLDIERTYPE *pSoldier )
  881. {
  882. if ( pSoldier->bTeam != gbPlayerNum )
  883. {
  884. return( FALSE );
  885. }
  886. if ( !pSoldier->bActive )
  887. {
  888. return( FALSE );
  889. }
  890. if ( !pSoldier->bInSector )
  891. {
  892. return( FALSE );
  893. }
  894. // Delete Light!
  895. DeleteSoldierLight( pSoldier );
  896. if ( pSoldier->iLight == -1 )
  897. {
  898. CreateSoldierLight( pSoldier );
  899. }
  900. return( TRUE );
  901. }
  902. BOOLEAN ReCreateSelectedSoldierLight( )
  903. {
  904. SOLDIERTYPE *pSoldier;
  905. if ( gusSelectedSoldier == NO_SOLDIER )
  906. {
  907. return( FALSE );
  908. }
  909. pSoldier = MercPtrs[ gusSelectedSoldier ];
  910. return( ReCreateSoldierLight( pSoldier ) );
  911. }
  912. BOOLEAN DeleteSoldierLight( SOLDIERTYPE *pSoldier )
  913. {
  914. if( pSoldier->iLight!=(-1) )
  915. {
  916. LightSpriteDestroy( pSoldier->iLight );
  917. pSoldier->iLight = -1;
  918. }
  919. return( TRUE );
  920. }
  921. // FUNCTIONS CALLED BY EVENT PUMP
  922. /////////////////////////////////
  923. BOOLEAN ChangeSoldierState( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT16 usStartingAniCode, BOOLEAN fForce )
  924. {
  925. EV_S_CHANGESTATE SChangeState;
  926. // Send message that we have changed states
  927. SChangeState.usNewState = usNewState;
  928. SChangeState.usSoldierID = pSoldier->ubID;
  929. SChangeState.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
  930. SChangeState.usStartingAniCode = usStartingAniCode;
  931. SChangeState.sXPos = pSoldier->sX;
  932. SChangeState.sYPos = pSoldier->sY;
  933. SChangeState.fForce = fForce;
  934. SChangeState.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
  935. //AddGameEvent( S_CHANGESTATE, 0, &SChangeState );
  936. EVENT_InitNewSoldierAnim( pSoldier, SChangeState.usNewState, SChangeState.usStartingAniCode, SChangeState.fForce );
  937. return( TRUE );
  938. }
  939. // This function reevaluates the stance if the guy sees us!
  940. BOOLEAN ReevaluateEnemyStance( SOLDIERTYPE *pSoldier, UINT16 usAnimState )
  941. {
  942. INT32 cnt, iClosestEnemy = NOBODY;
  943. INT16 sTargetXPos, sTargetYPos;
  944. BOOLEAN fReturnVal = FALSE;
  945. INT16 sDist, sClosestDist = 10000;
  946. // make the chosen one not turn to face us
  947. if ( OK_ENEMY_MERC( pSoldier ) && pSoldier->ubID != gTacticalStatus.ubTheChosenOne && gAnimControl[ usAnimState ].ubEndHeight == ANIM_STAND && !( pSoldier->uiStatusFlags & SOLDIER_UNDERAICONTROL) )
  948. {
  949. if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_OFF )
  950. {
  951. // If we are a queen and see enemies, goto ready
  952. if ( pSoldier->ubBodyType == QUEENMONSTER )
  953. {
  954. if ( gAnimControl[ usAnimState ].uiFlags & ( ANIM_BREATH ) )
  955. {
  956. if ( pSoldier->bOppCnt > 0 )
  957. {
  958. EVENT_InitNewSoldierAnim( pSoldier, QUEEN_INTO_READY, 0 , TRUE );
  959. return( TRUE );
  960. }
  961. }
  962. }
  963. // ATE: Don't do this if we're not a merc.....
  964. if ( !IS_MERC_BODY_TYPE( pSoldier ) )
  965. {
  966. return( FALSE );
  967. }
  968. if ( gAnimControl[ usAnimState ].uiFlags & ( ANIM_MERCIDLE | ANIM_BREATH ) )
  969. {
  970. if ( pSoldier->bOppCnt > 0 )
  971. {
  972. // Pick a guy this buddy sees and turn towards them!
  973. for ( cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++ )
  974. {
  975. if ( pSoldier->bOppList[ cnt ] == SEEN_CURRENTLY )
  976. {
  977. sDist = PythSpacesAway( pSoldier->sGridNo, MercPtrs[ cnt ]->sGridNo );
  978. if (sDist < sClosestDist)
  979. {
  980. sClosestDist = sDist;
  981. iClosestEnemy = cnt;
  982. }
  983. }
  984. }
  985. if (iClosestEnemy != NOBODY)
  986. {
  987. // Change to fire ready animation
  988. ConvertGridNoToXY( MercPtrs[ iClosestEnemy ]->sGridNo, &sTargetXPos, &sTargetYPos );
  989. pSoldier->fDontChargeReadyAPs = TRUE;
  990. // Ready weapon
  991. fReturnVal = SoldierReadyWeapon( pSoldier, sTargetXPos, sTargetYPos, FALSE );
  992. return( fReturnVal );
  993. }
  994. }
  995. }
  996. }
  997. }
  998. return( FALSE );
  999. }
  1000. void CheckForFreeupFromHit( SOLDIERTYPE *pSoldier, UINT32 uiOldAnimFlags, UINT32 uiNewAnimFlags, UINT16 usOldAniState, UINT16 usNewState )
  1001. {
  1002. // THIS COULD POTENTIALLY CALL EVENT_INITNEWAnim() if the GUY was SUPPRESSED
  1003. // CHECK IF THE OLD ANIMATION WAS A HIT START THAT WAS NOT FOLLOWED BY A HIT FINISH
  1004. // IF SO, RELEASE ATTACKER FROM ATTACKING
  1005. // If old and new animations are the same, do nothing!
  1006. if ( usOldAniState == QUEEN_HIT && usNewState == QUEEN_HIT )
  1007. {
  1008. return;
  1009. }
  1010. if ( usOldAniState != usNewState && ( uiOldAnimFlags & ANIM_HITSTART ) && !( uiNewAnimFlags & ANIM_HITFINISH ) && !( uiNewAnimFlags & ANIM_IGNOREHITFINISH ) && !(pSoldier->uiStatusFlags & SOLDIER_TURNINGFROMHIT ) )
  1011. {
  1012. // Release attacker
  1013. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Releasesoldierattacker, normal hit animation ended NEW: %s ( %d ) OLD: %s ( %d )", gAnimControl[ usNewState ].zAnimStr, usNewState, gAnimControl[ usOldAniState ].zAnimStr, pSoldier->usOldAniState ) );
  1014. ReleaseSoldiersAttacker( pSoldier );
  1015. //FREEUP GETTING HIT FLAG
  1016. pSoldier->fGettingHit = FALSE;
  1017. // ATE: if our guy, have 10% change of say damn, if still conscious...
  1018. if ( pSoldier->bTeam == gbPlayerNum && pSoldier->bLife >= OKLIFE )
  1019. {
  1020. if ( Random( 10 ) == 0 )
  1021. {
  1022. DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_CURSE1 ) );
  1023. }
  1024. }
  1025. }
  1026. // CHECK IF WE HAVE FINSIHED A HIT WHILE DOWN
  1027. // OBLY DO THIS IF 1 ) We are dead already or 2 ) We are alive still
  1028. if ( ( uiOldAnimFlags & ANIM_HITWHENDOWN ) && ( ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) || pSoldier->bLife != 0 ) )
  1029. {
  1030. // Release attacker
  1031. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Releasesoldierattacker, animation of kill on the ground ended") );
  1032. ReleaseSoldiersAttacker( pSoldier );
  1033. //FREEUP GETTING HIT FLAG
  1034. pSoldier->fGettingHit = FALSE;
  1035. if ( pSoldier->bLife == 0 )
  1036. {
  1037. //ATE: Set previous attacker's value!
  1038. // This is so that the killer can say their killed quote....
  1039. pSoldier->ubAttackerID = pSoldier->ubPreviousAttackerID;
  1040. }
  1041. }
  1042. }
  1043. // THIS IS CALLED FROM AN EVENT ( S_CHANGESTATE )!
  1044. BOOLEAN EVENT_InitNewSoldierAnim( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT16 usStartingAniCode, BOOLEAN fForce )
  1045. {
  1046. UINT16 usNewGridNo = 0;
  1047. INT16 sAPCost = 0;
  1048. INT16 sBPCost = 0;
  1049. UINT32 uiOldAnimFlags;
  1050. UINT32 uiNewAnimFlags;
  1051. UINT16 usSubState;
  1052. UINT16 usItem;
  1053. BOOLEAN fTryingToRestart = FALSE;
  1054. CHECKF( usNewState < NUMANIMATIONSTATES );
  1055. ///////////////////////////////////////////////////////////////////////
  1056. // DO SOME CHECKS ON OUR NEW ANIMATION!
  1057. /////////////////////////////////////////////////////////////////////
  1058. // If we are NOT loading a game, continue normally
  1059. if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
  1060. {
  1061. // CHECK IF WE ARE TRYING TO INTURRUPT A SCRIPT WHICH WE DO NOT WANT INTERRUPTED!
  1062. if ( pSoldier->fInNonintAnim )
  1063. {
  1064. return( FALSE );
  1065. }
  1066. if ( pSoldier->fRTInNonintAnim )
  1067. {
  1068. if ( !(gTacticalStatus.uiFlags & INCOMBAT) )
  1069. {
  1070. return( FALSE );
  1071. }
  1072. else
  1073. {
  1074. pSoldier->fRTInNonintAnim = FALSE;
  1075. }
  1076. }
  1077. // Check if we can restart this animation if it's the same as our current!
  1078. if ( usNewState == pSoldier->usAnimState )
  1079. {
  1080. if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_NORESTART ) && !fForce )
  1081. {
  1082. fTryingToRestart = TRUE;
  1083. }
  1084. }
  1085. // Check state, if we are not at the same height, set this ani as the pending one and
  1086. // change stance accordingly
  1087. // ATE: ONLY IF WE ARE STARTING AT START OF ANIMATION!
  1088. if ( usStartingAniCode == 0 )
  1089. {
  1090. if ( gAnimControl[ usNewState ].ubHeight != gAnimControl[ pSoldier->usAnimState ].ubEndHeight &&
  1091. !( gAnimControl[ usNewState ].uiFlags & ( ANIM_STANCECHANGEANIM | ANIM_IGNORE_AUTOSTANCE ) ) )
  1092. {
  1093. // Check if we are going from crouched height to prone height, and adjust fast turning accordingly
  1094. // Make guy turn while crouched THEN go into prone
  1095. if ( ( gAnimControl[ usNewState ].ubEndHeight == ANIM_PRONE && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH ) && !( gTacticalStatus.uiFlags & INCOMBAT ) )
  1096. {
  1097. pSoldier->fTurningUntilDone = TRUE;
  1098. pSoldier->ubPendingStanceChange = gAnimControl[ usNewState ].ubEndHeight;
  1099. pSoldier->usPendingAnimation = usNewState;
  1100. return( TRUE );
  1101. }
  1102. // Check if we are in realtime and we are going from stand to crouch
  1103. else if ( gAnimControl[ usNewState ].ubEndHeight == ANIM_CROUCH && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND && ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) && ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) )
  1104. {
  1105. pSoldier->ubDesiredHeight = gAnimControl[ usNewState ].ubEndHeight;
  1106. // Continue with this course of action IE: Do animation and skip from stand to crouch
  1107. }
  1108. // Check if we are in realtime and we are going from crouch to stand
  1109. else if ( gAnimControl[ usNewState ].ubEndHeight == ANIM_STAND && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH && ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) && ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) && pSoldier->usAnimState != HELIDROP )
  1110. {
  1111. pSoldier->ubDesiredHeight = gAnimControl[ usNewState ].ubEndHeight;
  1112. // Continue with this course of action IE: Do animation and skip from stand to crouch
  1113. }
  1114. else
  1115. {
  1116. // ONLY DO FOR EVERYONE BUT PLANNING GUYS
  1117. if ( pSoldier->ubID < MAX_NUM_SOLDIERS )
  1118. {
  1119. // Set our next moving animation to be pending, after
  1120. pSoldier->usPendingAnimation = usNewState;
  1121. // Set new state to be animation to move to new stance
  1122. SendChangeSoldierStanceEvent( pSoldier, gAnimControl[ usNewState ].ubHeight );
  1123. return( TRUE );
  1124. }
  1125. }
  1126. }
  1127. }
  1128. if ( usNewState == ADJACENT_GET_ITEM )
  1129. {
  1130. if ( pSoldier->ubPendingDirection != NO_PENDING_DIRECTION )
  1131. {
  1132. EVENT_InternalSetSoldierDesiredDirection( pSoldier, pSoldier->ubPendingDirection, FALSE, pSoldier->usAnimState );
  1133. pSoldier->ubPendingDirection = NO_PENDING_DIRECTION;
  1134. pSoldier->usPendingAnimation = ADJACENT_GET_ITEM;
  1135. pSoldier->fTurningUntilDone = TRUE;
  1136. SoldierGotoStationaryStance( pSoldier );
  1137. return( TRUE );
  1138. }
  1139. }
  1140. if ( usNewState == CLIMBUPROOF )
  1141. {
  1142. if ( pSoldier->ubPendingDirection != NO_PENDING_DIRECTION )
  1143. {
  1144. EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->ubPendingDirection );
  1145. pSoldier->ubPendingDirection = NO_PENDING_DIRECTION;
  1146. pSoldier->usPendingAnimation = CLIMBUPROOF;
  1147. pSoldier->fTurningUntilDone = TRUE;
  1148. SoldierGotoStationaryStance( pSoldier );
  1149. return( TRUE );
  1150. }
  1151. }
  1152. if ( usNewState == CLIMBDOWNROOF )
  1153. {
  1154. if ( pSoldier->ubPendingDirection != NO_PENDING_DIRECTION )
  1155. {
  1156. EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->ubPendingDirection );
  1157. pSoldier->ubPendingDirection = NO_PENDING_DIRECTION;
  1158. pSoldier->usPendingAnimation = CLIMBDOWNROOF;
  1159. pSoldier->fTurningFromPronePosition = FALSE;
  1160. pSoldier->fTurningUntilDone = TRUE;
  1161. SoldierGotoStationaryStance( pSoldier );
  1162. return( TRUE );
  1163. }
  1164. }
  1165. // ATE: Don't raise/lower automatically if we are low on health,
  1166. // as our gun looks lowered anyway....
  1167. //if ( pSoldier->bLife > INJURED_CHANGE_THREASHOLD )
  1168. {
  1169. // Don't do some of this if we are a monster!
  1170. // ATE: LOWER AIMATION IS GOOD, RAISE ONE HOWEVER MAY CAUSE PROBLEMS FOR AI....
  1171. if ( !(pSoldier->uiStatusFlags & SOLDIER_MONSTER ) && pSoldier->ubBodyType != ROBOTNOWEAPON && pSoldier->bTeam == gbPlayerNum )
  1172. {
  1173. // If this animation is a raise_weapon animation
  1174. if ( ( gAnimControl[ usNewState ].uiFlags & ANIM_RAISE_WEAPON ) && !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_RAISE_WEAPON | ANIM_NOCHANGE_WEAPON ) ) )
  1175. {
  1176. // We are told that we need to rasie weapon
  1177. // Do so only if
  1178. // 1) We have a rifle in hand...
  1179. usItem = pSoldier->inv[ HANDPOS ].usItem;
  1180. if ( usItem != NOTHING && (Item[ usItem ].fFlags & ITEM_TWO_HANDED) && usItem != ROCKET_LAUNCHER )
  1181. {
  1182. // Switch on height!
  1183. switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
  1184. {
  1185. case ANIM_STAND:
  1186. // 2) OK, all's fine... lower weapon first....
  1187. pSoldier->usPendingAnimation = usNewState;
  1188. // Set new state to be animation to move to new stance
  1189. usNewState = RAISE_RIFLE;
  1190. }
  1191. }
  1192. }
  1193. // If this animation is a lower_weapon animation
  1194. if ( ( gAnimControl[ usNewState ].uiFlags & ANIM_LOWER_WEAPON ) && !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_LOWER_WEAPON | ANIM_NOCHANGE_WEAPON ) ) )
  1195. {
  1196. // We are told that we need to rasie weapon
  1197. // Do so only if
  1198. // 1) We have a rifle in hand...
  1199. usItem = pSoldier->inv[ HANDPOS ].usItem;
  1200. if ( usItem != NOTHING && (Item[ usItem ].fFlags & ITEM_TWO_HANDED) && usItem != ROCKET_LAUNCHER )
  1201. {
  1202. // Switch on height!
  1203. switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
  1204. {
  1205. case ANIM_STAND:
  1206. // 2) OK, all's fine... lower weapon first....
  1207. pSoldier->usPendingAnimation = usNewState;
  1208. // Set new state to be animation to move to new stance
  1209. usNewState = LOWER_RIFLE;
  1210. }
  1211. }
  1212. }
  1213. }
  1214. }
  1215. // Are we cowering and are tyring to move, getup first...
  1216. if ( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING && pSoldier->usAnimState == COWERING && gAnimControl[ usNewState ].ubEndHeight == ANIM_STAND )
  1217. {
  1218. pSoldier->usPendingAnimation = usNewState;
  1219. // Set new state to be animation to move to new stance
  1220. usNewState = END_COWER;
  1221. }
  1222. // If we want to start swatting, put a pending animation
  1223. if( pSoldier->usAnimState != START_SWAT && usNewState == SWATTING )
  1224. {
  1225. // Set new state to be animation to move to new stance
  1226. usNewState = START_SWAT;
  1227. }
  1228. if( pSoldier->usAnimState == SWATTING && usNewState == CROUCHING )
  1229. {
  1230. // Set new state to be animation to move to new stance
  1231. usNewState = END_SWAT;
  1232. }
  1233. if( pSoldier->usAnimState == WALKING && usNewState == STANDING && pSoldier->bLife < INJURED_CHANGE_THREASHOLD && pSoldier->ubBodyType <= REGFEMALE && !MercInWater( pSoldier ) )
  1234. {
  1235. // Set new state to be animation to move to new stance
  1236. usNewState = END_HURT_WALKING;
  1237. }
  1238. // Check if we are an enemy, and we are in an animation what should be overriden
  1239. // by if he sees us or not.
  1240. if ( ReevaluateEnemyStance( pSoldier, usNewState ) )
  1241. {
  1242. return( TRUE );
  1243. }
  1244. // OK.......
  1245. if ( pSoldier->ubBodyType > REGFEMALE )
  1246. {
  1247. if ( pSoldier->bLife < INJURED_CHANGE_THREASHOLD )
  1248. {
  1249. if ( usNewState == READY_RIFLE_STAND )
  1250. {
  1251. // pSoldier->usPendingAnimation2 = usNewState;
  1252. // usNewState = FROM_INJURED_TRANSITION;
  1253. }
  1254. }
  1255. }
  1256. // Alrighty, check if we should free buddy up!
  1257. if ( usNewState == GIVING_AID )
  1258. {
  1259. UnSetUIBusy( pSoldier->ubID );
  1260. }
  1261. // SUBSTITUDE VARIOUS REG ANIMATIONS WITH ODD BODY TYPES
  1262. if ( SubstituteBodyTypeAnimation( pSoldier, usNewState, &usSubState ) )
  1263. {
  1264. usNewState = usSubState;
  1265. }
  1266. // CHECK IF WE CAN DO THIS ANIMATION!
  1267. if ( IsAnimationValidForBodyType( pSoldier, usNewState ) == FALSE )
  1268. {
  1269. return( FALSE );
  1270. }
  1271. // OK, make guy transition if a big merc...
  1272. if ( pSoldier->uiAnimSubFlags & SUB_ANIM_BIGGUYTHREATENSTANCE )
  1273. {
  1274. if ( usNewState == KNEEL_DOWN && pSoldier->usAnimState != BIGMERC_CROUCH_TRANS_INTO )
  1275. {
  1276. UINT16 usItem;
  1277. // Do we have a rifle?
  1278. usItem = pSoldier->inv[ HANDPOS ].usItem;
  1279. if ( usItem != NOTHING )
  1280. {
  1281. if ( Item[ usItem ].usItemClass == IC_GUN && usItem != ROCKET_LAUNCHER )
  1282. {
  1283. if ( (Item[ usItem ].fFlags & ITEM_TWO_HANDED) )
  1284. {
  1285. usNewState = BIGMERC_CROUCH_TRANS_INTO;
  1286. }
  1287. }
  1288. }
  1289. }
  1290. if ( usNewState == KNEEL_UP && pSoldier->usAnimState != BIGMERC_CROUCH_TRANS_OUTOF )
  1291. {
  1292. UINT16 usItem;
  1293. // Do we have a rifle?
  1294. usItem = pSoldier->inv[ HANDPOS ].usItem;
  1295. if ( usItem != NOTHING )
  1296. {
  1297. if ( Item[ usItem ].usItemClass == IC_GUN && usItem != ROCKET_LAUNCHER )
  1298. {
  1299. if ( (Item[ usItem ].fFlags & ITEM_TWO_HANDED) )
  1300. {
  1301. usNewState = BIGMERC_CROUCH_TRANS_OUTOF;
  1302. }
  1303. }
  1304. }
  1305. }
  1306. }
  1307. // OK, if we have reverse set, do the side step!
  1308. if ( pSoldier->bReverse )
  1309. {
  1310. if ( usNewState == WALKING || usNewState == RUNNING || usNewState == SWATTING )
  1311. {
  1312. // CHECK FOR SIDEWAYS!
  1313. if ( pSoldier->bDirection == gPurpendicularDirection[ pSoldier->bDirection ][ pSoldier->usPathingData[ pSoldier->usPathIndex ] ] )
  1314. {
  1315. // We are perpendicular!
  1316. usNewState = SIDE_STEP;
  1317. }
  1318. else
  1319. {
  1320. if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH )
  1321. {
  1322. usNewState = SWAT_BACKWARDS;
  1323. }
  1324. else
  1325. {
  1326. // Here, change to opposite direction
  1327. usNewState = WALK_BACKWARDS;
  1328. }
  1329. }
  1330. }
  1331. }
  1332. // ATE: Patch hole for breath collapse for roofs, fences
  1333. if ( usNewState == CLIMBUPROOF || usNewState == CLIMBDOWNROOF || usNewState == HOPFENCE )
  1334. {
  1335. // Check for breath collapse if a given animation like
  1336. if ( CheckForBreathCollapse( pSoldier ) || pSoldier->bCollapsed )
  1337. {
  1338. // UNset UI
  1339. UnSetUIBusy( pSoldier->ubID );
  1340. SoldierCollapse( pSoldier );
  1341. pSoldier->bBreathCollapsed = FALSE;
  1342. return( FALSE );
  1343. }
  1344. }
  1345. // If we are in water.....and trying to run, change to run
  1346. if ( pSoldier->bOverTerrainType == LOW_WATER || pSoldier->bOverTerrainType == MED_WATER )
  1347. {
  1348. // Check animation
  1349. // Change to walking
  1350. if ( usNewState == RUNNING )
  1351. {
  1352. usNewState = WALKING;
  1353. }
  1354. }
  1355. // Turn off anipause flag for any anim!
  1356. pSoldier->uiStatusFlags &= (~SOLDIER_PAUSEANIMOVE);
  1357. // Unset paused for no APs.....
  1358. AdjustNoAPToFinishMove( pSoldier, FALSE );
  1359. if ( usNewState == CRAWLING && pSoldier->usDontUpdateNewGridNoOnMoveAnimChange == 1 )
  1360. {
  1361. if ( pSoldier->fTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE )
  1362. {
  1363. pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_START_UP_FROM_MOVE;
  1364. }
  1365. // ATE: IF we are starting to crawl, but have to getup to turn first......
  1366. if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_START_UP_FROM_MOVE )
  1367. {
  1368. usNewState = PRONE_UP;
  1369. pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE;
  1370. }
  1371. }
  1372. // We are about to start moving
  1373. // Handle buddy beginning to move...
  1374. // check new gridno, etc
  1375. // ATE: Added: Make check that old anim is not a moving one as well
  1376. if ( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING && !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) || ( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING && fForce ) )
  1377. {
  1378. BOOLEAN fKeepMoving;
  1379. if ( usNewState == CRAWLING && pSoldier->usDontUpdateNewGridNoOnMoveAnimChange == LOCKED_NO_NEWGRIDNO )
  1380. {
  1381. // Turn off lock once we are crawling once...
  1382. pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = 1;
  1383. }
  1384. // ATE: Additional check here if we have just been told to update animation ONLY, not goto gridno stuff...
  1385. if ( !pSoldier->usDontUpdateNewGridNoOnMoveAnimChange )
  1386. {
  1387. if ( usNewState != SWATTING )
  1388. {
  1389. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Handling New gridNo for %d: Old %s, New %s", pSoldier->ubID, gAnimControl[ pSoldier->usAnimState ].zAnimStr , gAnimControl[ usNewState ].zAnimStr) );
  1390. if ( !( gAnimControl[ usNewState ].uiFlags & ANIM_SPECIALMOVE ) )
  1391. {
  1392. // Handle goto new tile...
  1393. if ( HandleGotoNewGridNo( pSoldier, &fKeepMoving, TRUE, usNewState ) )
  1394. {
  1395. if ( !fKeepMoving )
  1396. {
  1397. return( FALSE );
  1398. }
  1399. // Make sure desy = zeroed out...
  1400. // pSoldier->fPastXDest = pSoldier->fPastYDest = FALSE;
  1401. }
  1402. else
  1403. {
  1404. if ( pSoldier->bBreathCollapsed )
  1405. {
  1406. // UNset UI
  1407. UnSetUIBusy( pSoldier->ubID );
  1408. SoldierCollapse( pSoldier );
  1409. pSoldier->bBreathCollapsed = FALSE;
  1410. }
  1411. return( FALSE );
  1412. }
  1413. }
  1414. else
  1415. {
  1416. // Change desired direction
  1417. // Just change direction
  1418. EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, pSoldier->usAnimState );
  1419. }
  1420. //check for services
  1421. ReceivingSoldierCancelServices( pSoldier );
  1422. GivingSoldierCancelServices( pSoldier );
  1423. // Check if we are a vehicle, and start playing noise sound....
  1424. if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
  1425. {
  1426. HandleVehicleMovementSound( pSoldier, TRUE );
  1427. }
  1428. }
  1429. }
  1430. }
  1431. else
  1432. {
  1433. // Check for stopping movement noise...
  1434. if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
  1435. {
  1436. HandleVehicleMovementSound( pSoldier, FALSE );
  1437. // If a vehicle, set hewight to 0
  1438. SetSoldierHeight( pSoldier, (FLOAT)( 0 ) );
  1439. }
  1440. }
  1441. // Reset to false always.....
  1442. // ( Unless locked )
  1443. if ( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING )
  1444. {
  1445. if ( pSoldier->usDontUpdateNewGridNoOnMoveAnimChange != LOCKED_NO_NEWGRIDNO )
  1446. {
  1447. pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = FALSE;
  1448. }
  1449. }
  1450. if ( fTryingToRestart )
  1451. {
  1452. return( FALSE );
  1453. }
  1454. }
  1455. // ATE: If this is an AI guy.. unlock him!
  1456. if ( gTacticalStatus.fEnemySightingOnTheirTurn )
  1457. {
  1458. if ( gTacticalStatus.ubEnemySightingOnTheirTurnEnemyID == pSoldier->ubID )
  1459. {
  1460. pSoldier->fPauseAllAnimation = FALSE;
  1461. gTacticalStatus.fEnemySightingOnTheirTurn = FALSE;
  1462. }
  1463. }
  1464. ///////////////////////////////////////////////////////////////////////
  1465. // HERE DOWN - WE HAVE MADE A DESCISION!
  1466. /////////////////////////////////////////////////////////////////////
  1467. uiOldAnimFlags = gAnimControl[ pSoldier->usAnimState ].uiFlags;
  1468. uiNewAnimFlags = gAnimControl[ usNewState ].uiFlags;
  1469. usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc( pSoldier->usPathingData[ pSoldier->usPathIndex ] ) );
  1470. // CHECKING IF WE HAVE A HIT FINISH BUT NO DEATH IS DONE WITH A SPECIAL ANI CODE
  1471. // IN THE HIT FINSIH ANI SCRIPTS
  1472. // CHECKING IF WE HAVE FINISHED A DEATH ANIMATION IS DONE WITH A SPECIAL ANI CODE
  1473. // IN THE DEATH SCRIPTS
  1474. // CHECK IF THIS NEW STATE IS NON-INTERRUPTABLE
  1475. // IF SO - SET NON-INT FLAG
  1476. if ( uiNewAnimFlags & ANIM_NONINTERRUPT )
  1477. {
  1478. pSoldier->fInNonintAnim = TRUE;
  1479. }
  1480. if ( uiNewAnimFlags & ANIM_RT_NONINTERRUPT )
  1481. {
  1482. pSoldier->fRTInNonintAnim = TRUE;
  1483. }
  1484. // CHECK IF WE ARE NOT AIMING, IF NOT, RESET LAST TAGRET!
  1485. if ( !(gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY ) && !(gAnimControl[ usNewState ].uiFlags & ANIM_FIREREADY ) )
  1486. {
  1487. // ATE: Also check for the transition anims to not reset this
  1488. // this should have used a flag but we're out of them....
  1489. if ( usNewState != READY_RIFLE_STAND && usNewState != READY_RIFLE_PRONE && usNewState != READY_RIFLE_CROUCH && usNewState != ROBOT_SHOOT )
  1490. {
  1491. pSoldier->sLastTarget = NOWHERE;
  1492. }
  1493. }
  1494. // If a special move state, release np aps
  1495. if ( ( gAnimControl[ usNewState ].uiFlags & ANIM_SPECIALMOVE ) )
  1496. {
  1497. AdjustNoAPToFinishMove( pSoldier, FALSE );
  1498. }
  1499. if ( gAnimControl[ usNewState ].uiFlags & ANIM_UPDATEMOVEMENTMODE )
  1500. {
  1501. if ( pSoldier->bTeam == gbPlayerNum )
  1502. {
  1503. // pSoldier->usUIMovementMode = GetMoveStateBasedOnStance( pSoldier, gAnimControl[ usNewState ].ubEndHeight );
  1504. }
  1505. }
  1506. // ATE: If not a moving animation - turn off reverse....
  1507. if ( !( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING ) )
  1508. {
  1509. pSoldier->bReverse = FALSE;
  1510. }
  1511. // ONLY DO FOR EVERYONE BUT PLANNING GUYS
  1512. if ( pSoldier->ubID < MAX_NUM_SOLDIERS )
  1513. {
  1514. // Do special things based on new state
  1515. switch( usNewState )
  1516. {
  1517. case STANDING:
  1518. // Update desired height
  1519. pSoldier->ubDesiredHeight = ANIM_STAND;
  1520. break;
  1521. case CROUCHING:
  1522. // Update desired height
  1523. pSoldier->ubDesiredHeight = ANIM_CROUCH;
  1524. break;
  1525. case PRONE:
  1526. // Update desired height
  1527. pSoldier->ubDesiredHeight = ANIM_PRONE;
  1528. break;
  1529. case READY_RIFLE_STAND:
  1530. case READY_RIFLE_PRONE:
  1531. case READY_RIFLE_CROUCH:
  1532. case READY_DUAL_STAND:
  1533. case READY_DUAL_CROUCH:
  1534. case READY_DUAL_PRONE:
  1535. // OK, get points to ready weapon....
  1536. if ( !pSoldier->fDontChargeReadyAPs )
  1537. {
  1538. sAPCost = GetAPsToReadyWeapon( pSoldier, usNewState );
  1539. DeductPoints( pSoldier, sAPCost, sBPCost );
  1540. }
  1541. else
  1542. {
  1543. pSoldier->fDontChargeReadyAPs = FALSE;
  1544. }
  1545. break;
  1546. case WALKING:
  1547. pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
  1548. pSoldier->ubPendingActionAnimCount = 0;
  1549. break;
  1550. case SWATTING:
  1551. pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
  1552. pSoldier->ubPendingActionAnimCount = 0;
  1553. break;
  1554. case CRAWLING:
  1555. // Turn off flag...
  1556. pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
  1557. pSoldier->ubPendingActionAnimCount = 0;
  1558. pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
  1559. break;
  1560. case RUNNING:
  1561. // Only if our previous is not running
  1562. if ( pSoldier->usAnimState != RUNNING )
  1563. {
  1564. sAPCost = AP_START_RUN_COST;
  1565. DeductPoints( pSoldier, sAPCost, sBPCost );
  1566. }
  1567. // Set pending action count to 0
  1568. pSoldier->ubPendingActionAnimCount = 0;
  1569. pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
  1570. break;
  1571. case ADULTMONSTER_WALKING:
  1572. pSoldier->ubPendingActionAnimCount = 0;
  1573. break;
  1574. case ROBOT_WALK:
  1575. pSoldier->ubPendingActionAnimCount = 0;
  1576. break;
  1577. case KNEEL_UP:
  1578. case KNEEL_DOWN:
  1579. case BIGMERC_CROUCH_TRANS_INTO:
  1580. case BIGMERC_CROUCH_TRANS_OUTOF:
  1581. if ( !pSoldier->fDontChargeAPsForStanceChange )
  1582. {
  1583. DeductPoints( pSoldier, AP_CROUCH, BP_CROUCH );
  1584. }
  1585. pSoldier->fDontChargeAPsForStanceChange = FALSE;
  1586. break;
  1587. case PRONE_UP:
  1588. case PRONE_DOWN:
  1589. // ATE: If we are NOT waiting for prone down...
  1590. if ( pSoldier->fTurningFromPronePosition < TURNING_FROM_PRONE_START_UP_FROM_MOVE && !pSoldier->fDontChargeAPsForStanceChange )
  1591. {
  1592. // ATE: Don't do this if we are still 'moving'....
  1593. if ( pSoldier->sGridNo == pSoldier->sFinalDestination || pSoldier->usPathIndex == 0 )
  1594. {
  1595. DeductPoints( pSoldier, AP_PRONE, BP_PRONE );
  1596. }
  1597. }
  1598. pSoldier->fDontChargeAPsForStanceChange = FALSE;
  1599. break;
  1600. //Deduct points for stance change
  1601. //sAPCost = GetAPsToChangeStance( pSoldier, gAnimControl[ usNewState ].ubEndHeight );
  1602. //DeductPoints( pSoldier, sAPCost, 0 );
  1603. //break;
  1604. case START_AID:
  1605. DeductPoints( pSoldier, AP_START_FIRST_AID, BP_START_FIRST_AID );
  1606. break;
  1607. case CUTTING_FENCE:
  1608. DeductPoints( pSoldier, AP_USEWIRECUTTERS, BP_USEWIRECUTTERS );
  1609. break;
  1610. case PLANT_BOMB:
  1611. DeductPoints( pSoldier, AP_DROP_BOMB, 0 );
  1612. break;
  1613. case STEAL_ITEM:
  1614. DeductPoints( pSoldier, AP_STEAL_ITEM, 0 );
  1615. break;
  1616. case CROW_DIE:
  1617. // Delete shadow of crow....
  1618. if ( pSoldier->pAniTile != NULL )
  1619. {
  1620. DeleteAniTile( pSoldier->pAniTile );
  1621. pSoldier->pAniTile = NULL;
  1622. }
  1623. break;
  1624. case CROW_FLY:
  1625. // Ate: startup a shadow ( if gridno is set )
  1626. HandleCrowShadowNewGridNo( pSoldier );
  1627. break;
  1628. case CROW_EAT:
  1629. // ATE: Make sure height level is 0....
  1630. SetSoldierHeight( pSoldier, (FLOAT)(0) );
  1631. HandleCrowShadowRemoveGridNo( pSoldier );
  1632. break;
  1633. case USE_REMOTE:
  1634. DeductPoints( pSoldier, AP_USE_REMOTE, 0 );
  1635. break;
  1636. //case PUNCH:
  1637. //Deduct points for punching
  1638. //sAPCost = MinAPsToAttack( pSoldier, pSoldier->sGridNo, FALSE );
  1639. //DeductPoints( pSoldier, sAPCost, 0 );
  1640. //break;
  1641. case HOPFENCE:
  1642. DeductPoints( pSoldier, AP_JUMPFENCE, BP_JUMPFENCE );
  1643. break;
  1644. // Deduct aps for falling down....
  1645. case FALLBACK_HIT_STAND:
  1646. case FALLFORWARD_FROMHIT_STAND:
  1647. DeductPoints( pSoldier, AP_FALL_DOWN, BP_FALL_DOWN );
  1648. break;
  1649. case FALLFORWARD_FROMHIT_CROUCH:
  1650. DeductPoints( pSoldier, (AP_FALL_DOWN/2), (BP_FALL_DOWN/2) );
  1651. break;
  1652. case QUEEN_SWIPE:
  1653. // ATE: set damage counter...
  1654. pSoldier->uiPendingActionData1 = 0;
  1655. break;
  1656. case CLIMBDOWNROOF:
  1657. // disable sight
  1658. gTacticalStatus.uiFlags |= DISALLOW_SIGHT;
  1659. DeductPoints( pSoldier, AP_CLIMBOFFROOF, BP_CLIMBOFFROOF );
  1660. break;
  1661. case CLIMBUPROOF:
  1662. // disable sight
  1663. gTacticalStatus.uiFlags |= DISALLOW_SIGHT;
  1664. DeductPoints( pSoldier, AP_CLIMBROOF, BP_CLIMBROOF );
  1665. break;
  1666. case JUMP_OVER_BLOCKING_PERSON:
  1667. // Set path....
  1668. {
  1669. UINT16 usNewGridNo;
  1670. DeductPoints( pSoldier, AP_JUMP_OVER, BP_JUMP_OVER );
  1671. usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( pSoldier->bDirection ) );
  1672. usNewGridNo = NewGridNo( (UINT16)usNewGridNo, DirectionInc( pSoldier->bDirection ) );
  1673. pSoldier->usPathDataSize = 0;
  1674. pSoldier->usPathIndex = 0;
  1675. pSoldier->usPathingData[ pSoldier->usPathDataSize ] = pSoldier->bDirection;
  1676. pSoldier->usPathDataSize++;
  1677. pSoldier->usPathingData[ pSoldier->usPathDataSize ] = pSoldier->bDirection;
  1678. pSoldier->usPathDataSize++;
  1679. pSoldier->sFinalDestination = usNewGridNo;
  1680. // Set direction
  1681. EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, JUMP_OVER_BLOCKING_PERSON );
  1682. }
  1683. break;
  1684. case GENERIC_HIT_STAND:
  1685. case GENERIC_HIT_CROUCH:
  1686. case STANDING_BURST_HIT:
  1687. case ADULTMONSTER_HIT:
  1688. case ADULTMONSTER_DYING:
  1689. case COW_HIT:
  1690. case COW_DYING:
  1691. case BLOODCAT_HIT:
  1692. case BLOODCAT_DYING:
  1693. case WATER_HIT:
  1694. case WATER_DIE:
  1695. case DEEP_WATER_HIT:
  1696. case DEEP_WATER_DIE:
  1697. case RIFLE_STAND_HIT:
  1698. case LARVAE_HIT:
  1699. case LARVAE_DIE:
  1700. case QUEEN_HIT:
  1701. case QUEEN_DIE:
  1702. case INFANT_HIT:
  1703. case INFANT_DIE:
  1704. case CRIPPLE_HIT:
  1705. case CRIPPLE_DIE:
  1706. case CRIPPLE_DIE_FLYBACK:
  1707. case ROBOTNW_HIT:
  1708. case ROBOTNW_DIE:
  1709. // Set getting hit flag to TRUE
  1710. pSoldier->fGettingHit = TRUE;
  1711. break;
  1712. case CHARIOTS_OF_FIRE:
  1713. case BODYEXPLODING:
  1714. // Merc on fire!
  1715. pSoldier->uiPendingActionData1 = PlaySoldierJA2Sample( pSoldier->ubID, ( FIRE_ON_MERC ), RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 5, SoundDir( pSoldier->sGridNo ), TRUE );
  1716. break;
  1717. }
  1718. }
  1719. // Remove old animation profile
  1720. HandleAnimationProfile( pSoldier, pSoldier->usAnimState, TRUE );
  1721. // From animation control, set surface
  1722. if ( SetSoldierAnimationSurface( pSoldier, usNewState ) == FALSE )
  1723. {
  1724. return( FALSE );
  1725. }
  1726. // Set state
  1727. pSoldier->usOldAniState = pSoldier->usAnimState;
  1728. pSoldier->sOldAniCode = pSoldier->usAniCode;
  1729. // Change state value!
  1730. pSoldier->usAnimState = usNewState;
  1731. pSoldier->sZLevelOverride = -1;
  1732. if ( !( pSoldier->uiStatusFlags & SOLDIER_LOCKPENDINGACTIONCOUNTER ) )
  1733. {
  1734. //ATE Cancel ANY pending action...
  1735. if ( pSoldier->ubPendingActionAnimCount > 0 && ( gAnimControl[ pSoldier->usOldAniState ].uiFlags & ANIM_MOVING ) )
  1736. {
  1737. // Do some special things for some actions
  1738. switch( pSoldier->ubPendingAction )
  1739. {
  1740. case MERC_GIVEITEM:
  1741. // Unset target as enaged
  1742. MercPtrs[ pSoldier->uiPendingActionData4 ]->uiStatusFlags &= (~SOLDIER_ENGAGEDINACTION);
  1743. break;
  1744. }
  1745. pSoldier->ubPendingAction = NO_PENDING_ACTION;
  1746. }
  1747. else
  1748. {
  1749. // Increment this for almost all animations except some movement ones...
  1750. // That's because this represents ANY animation other than the one we began when the pending action was started
  1751. // ATE: Added to ignore this count if we are waiting for someone to move out of our way...
  1752. if ( usNewState != START_SWAT && usNewState != END_SWAT && !( gAnimControl[ usNewState ].uiFlags & ANIM_NOCHANGE_PENDINGCOUNT ) && !pSoldier->fDelayedMovement && !( pSoldier->uiStatusFlags & SOLDIER_ENGAGEDINACTION ) )
  1753. {
  1754. pSoldier->ubPendingActionAnimCount++;
  1755. }
  1756. }
  1757. }
  1758. // Set new animation profile
  1759. HandleAnimationProfile( pSoldier, usNewState, FALSE );
  1760. // Reset some animation values
  1761. pSoldier->fForceShade = FALSE;
  1762. CheckForFreeupFromHit( pSoldier, uiOldAnimFlags, uiNewAnimFlags, pSoldier->usOldAniState, usNewState );
  1763. // CHECK IF WE ARE AT AN IDLE ACTION
  1764. #if 0
  1765. if ( gAnimControl[ usNewState ].uiFlags & ANIM_IDLE )
  1766. {
  1767. pSoldier->bAction = ACTION_DONE;
  1768. }
  1769. else
  1770. {
  1771. pSoldier->bAction = ACTION_BUSY;
  1772. }
  1773. #endif
  1774. // Set current frame
  1775. pSoldier->usAniCode = usStartingAniCode;
  1776. // ATE; For some animations that could use some variations, do so....
  1777. if (usNewState == CHARIOTS_OF_FIRE || usNewState == BODYEXPLODING )
  1778. {
  1779. pSoldier->usAniCode = (UINT16)( Random( 10 ) );
  1780. }
  1781. // ATE: Default to first frame....
  1782. // Will get changed ( probably ) by AdjustToNextAnimationFrame()
  1783. ConvertAniCodeToAniFrame( pSoldier, (INT16)( 0 ) );
  1784. // Set delay speed
  1785. SetSoldierAniSpeed( pSoldier );
  1786. // Reset counters
  1787. RESETTIMECOUNTER( pSoldier->UpdateCounter, pSoldier->sAniDelay );
  1788. // Adjust to new animation frame ( the first one )
  1789. AdjustToNextAnimationFrame( pSoldier );
  1790. // Setup offset information for UI above guy
  1791. SetSoldierLocatorOffsets( pSoldier );
  1792. // If our own guy...
  1793. if ( pSoldier->bTeam == gbPlayerNum )
  1794. {
  1795. // Are we stationary?
  1796. if ( gAnimControl[ usNewState ].uiFlags & ANIM_STATIONARY )
  1797. {
  1798. // Position light....
  1799. // SetCheckSoldierLightFlag( pSoldier );
  1800. }
  1801. else
  1802. {
  1803. // Hide light.....
  1804. // DeleteSoldierLight( pSoldier );
  1805. }
  1806. }
  1807. // If we are certain animations, reload palette
  1808. if ( usNewState == VEHICLE_DIE || usNewState == CHARIOTS_OF_FIRE || usNewState == BODYEXPLODING )
  1809. {
  1810. CreateSoldierPalettes( pSoldier );
  1811. }
  1812. // ATE: if the old animation was a movement, and new is not, play sound...
  1813. // OK, play final footstep sound...
  1814. if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
  1815. {
  1816. if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_STATIONARY ) &&
  1817. ( gAnimControl[ pSoldier->usOldAniState ].uiFlags & ANIM_MOVING ) )
  1818. {
  1819. PlaySoldierFootstepSound( pSoldier );
  1820. }
  1821. }
  1822. // Free up from stance change
  1823. FreeUpNPCFromStanceChange( pSoldier );
  1824. return( TRUE );
  1825. }
  1826. void InternalRemoveSoldierFromGridNo( SOLDIERTYPE *pSoldier, BOOLEAN fForce )
  1827. {
  1828. INT8 bDir;
  1829. INT32 iGridNo;
  1830. if((pSoldier->sGridNo!=NO_MAP_POS) )
  1831. {
  1832. if ( pSoldier->bInSector || fForce )
  1833. {
  1834. // Remove from world ( old pos )
  1835. RemoveMerc( pSoldier->sGridNo, pSoldier, FALSE );
  1836. HandleAnimationProfile( pSoldier, pSoldier->usAnimState, TRUE );
  1837. // Remove records of this guy being adjacent
  1838. for (bDir = 0; bDir < NUM_WORLD_DIRECTIONS; bDir++)
  1839. {
  1840. iGridNo = pSoldier->sGridNo + DirIncrementer[ bDir ];
  1841. if( iGridNo >= 0 && iGridNo < WORLD_MAX )
  1842. {
  1843. gpWorldLevelData[ iGridNo ].ubAdjacentSoldierCnt--;
  1844. }
  1845. }
  1846. HandlePlacingRoofMarker( pSoldier, pSoldier->sGridNo, FALSE , FALSE );
  1847. // Remove reseved movement value
  1848. UnMarkMovementReserved( pSoldier );
  1849. HandleCrowShadowRemoveGridNo( pSoldier );
  1850. // Reset gridno...
  1851. pSoldier->sGridNo = NO_MAP_POS;
  1852. }
  1853. }
  1854. }
  1855. void RemoveSoldierFromGridNo( SOLDIERTYPE *pSoldier )
  1856. {
  1857. InternalRemoveSoldierFromGridNo( pSoldier, FALSE );
  1858. }
  1859. void EVENT_InternalSetSoldierPosition( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos ,BOOLEAN fUpdateDest, BOOLEAN fUpdateFinalDest, BOOLEAN fForceRemove )
  1860. {
  1861. INT16 sNewGridNo;
  1862. // Not if we're dead!
  1863. if ( ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) )
  1864. {
  1865. return;
  1866. }
  1867. // Set new map index
  1868. sNewGridNo = GETWORLDINDEXFROMWORLDCOORDS(dNewYPos, dNewXPos );
  1869. if ( fUpdateDest )
  1870. {
  1871. pSoldier->sDestination = sNewGridNo;
  1872. }
  1873. if ( fUpdateFinalDest )
  1874. {
  1875. pSoldier->sFinalDestination = sNewGridNo;
  1876. }
  1877. // Copy old values
  1878. pSoldier->dOldXPos = pSoldier->dXPos;
  1879. pSoldier->dOldYPos = pSoldier->dYPos;
  1880. // Set New pos
  1881. pSoldier->dXPos = dNewXPos;
  1882. pSoldier->dYPos = dNewYPos;
  1883. pSoldier->sX = (INT16)dNewXPos;
  1884. pSoldier->sY = (INT16)dNewYPos;
  1885. HandleCrowShadowNewPosition( pSoldier );
  1886. SetSoldierGridNo( pSoldier, sNewGridNo, fForceRemove );
  1887. if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) )
  1888. {
  1889. if ( gGameSettings.fOptions[ TOPTION_MERC_ALWAYS_LIGHT_UP ] )
  1890. {
  1891. SetCheckSoldierLightFlag( pSoldier );
  1892. }
  1893. }
  1894. // ATE: Mirror calls if we are a vehicle ( for all our passengers )
  1895. UpdateAllVehiclePassengersGridNo( pSoldier );
  1896. }
  1897. void EVENT_SetSoldierPosition( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos )
  1898. {
  1899. EVENT_InternalSetSoldierPosition( pSoldier, dNewXPos, dNewYPos ,TRUE, TRUE, FALSE );
  1900. }
  1901. void EVENT_SetSoldierPositionForceDelete( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos )
  1902. {
  1903. EVENT_InternalSetSoldierPosition( pSoldier, dNewXPos, dNewYPos ,TRUE, TRUE, TRUE );
  1904. }
  1905. void EVENT_SetSoldierPositionAndMaybeFinalDest( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos, BOOLEAN fUpdateFinalDest )
  1906. {
  1907. EVENT_InternalSetSoldierPosition( pSoldier, dNewXPos, dNewYPos ,TRUE, fUpdateFinalDest, FALSE );
  1908. }
  1909. void EVENT_SetSoldierPositionAndMaybeFinalDestAndMaybeNotDestination( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos, BOOLEAN fUpdateDest, BOOLEAN fUpdateFinalDest )
  1910. {
  1911. EVENT_InternalSetSoldierPosition( pSoldier, dNewXPos, dNewYPos ,fUpdateDest, fUpdateFinalDest, FALSE );
  1912. }
  1913. void InternalSetSoldierHeight( SOLDIERTYPE *pSoldier, FLOAT dNewHeight, BOOLEAN fUpdateLevel )
  1914. {
  1915. INT8 bOldLevel = pSoldier->bLevel;
  1916. pSoldier->dHeightAdjustment = dNewHeight;
  1917. pSoldier->sHeightAdjustment = (INT16)pSoldier->dHeightAdjustment;
  1918. if ( !fUpdateLevel )
  1919. {
  1920. return;
  1921. }
  1922. if ( pSoldier->sHeightAdjustment > 0 )
  1923. {
  1924. pSoldier->bLevel = SECOND_LEVEL;
  1925. ApplyTranslucencyToWalls((INT16)(pSoldier->dXPos/CELL_X_SIZE), (INT16)(pSoldier->dYPos/CELL_Y_SIZE));
  1926. //LightHideTrees((INT16)(pSoldier->dXPos/CELL_X_SIZE), (INT16)(pSoldier->dYPos/CELL_Y_SIZE));
  1927. //ConcealAllWalls();
  1928. //pSoldier->pLevelNode->ubShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubShadeLevel;
  1929. //pSoldier->pLevelNode->ubSumLights=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubSumLights;
  1930. //pSoldier->pLevelNode->ubMaxLights=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubMaxLights;
  1931. //pSoldier->pLevelNode->ubNaturalShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubNaturalShadeLevel;
  1932. }
  1933. else
  1934. {
  1935. pSoldier->bLevel = FIRST_LEVEL;
  1936. //pSoldier->pLevelNode->ubShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubShadeLevel;
  1937. //pSoldier->pLevelNode->ubSumLights=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubSumLights;
  1938. //pSoldier->pLevelNode->ubMaxLights=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubMaxLights;
  1939. //pSoldier->pLevelNode->ubNaturalShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubNaturalShadeLevel;
  1940. }
  1941. if ( bOldLevel == 0 && pSoldier->bLevel == 0 )
  1942. {
  1943. }
  1944. else
  1945. {
  1946. // Show room at new level
  1947. //HideRoom( pSoldier->sGridNo, pSoldier );
  1948. }
  1949. }
  1950. void SetSoldierHeight( SOLDIERTYPE *pSoldier, FLOAT dNewHeight )
  1951. {
  1952. InternalSetSoldierHeight( pSoldier, dNewHeight, TRUE );
  1953. }
  1954. void SetSoldierGridNo( SOLDIERTYPE *pSoldier, INT16 sNewGridNo, BOOLEAN fForceRemove )
  1955. {
  1956. BOOLEAN fInWaterValue;
  1957. INT8 bDir;
  1958. INT32 cnt;
  1959. SOLDIERTYPE * pEnemy;
  1960. //INT16 sX, sY, sWorldX, sZLevel;
  1961. // Not if we're dead!
  1962. if ( ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) )
  1963. {
  1964. return;
  1965. }
  1966. if ( sNewGridNo != pSoldier->sGridNo || pSoldier->pLevelNode == NULL )
  1967. {
  1968. // Check if we are moving AND this is our next dest gridno....
  1969. if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_MOVING | ANIM_SPECIALMOVE ) )
  1970. {
  1971. if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
  1972. {
  1973. if ( sNewGridNo != pSoldier->sDestination )
  1974. {
  1975. // THIS MUST be our new one......MAKE IT SO
  1976. sNewGridNo = pSoldier->sDestination;
  1977. }
  1978. // Now check this baby....
  1979. if ( sNewGridNo == pSoldier->sGridNo )
  1980. {
  1981. return;
  1982. }
  1983. }
  1984. }
  1985. pSoldier->sOldGridNo = pSoldier->sGridNo;
  1986. if ( pSoldier->ubBodyType == QUEENMONSTER )
  1987. {
  1988. SetPositionSndGridNo( pSoldier->iPositionSndID, sNewGridNo );
  1989. }
  1990. if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) )
  1991. {
  1992. InternalRemoveSoldierFromGridNo( pSoldier, fForceRemove );
  1993. }
  1994. // CHECK IF OUR NEW GIRDNO IS VALID,IF NOT DONOT SET!
  1995. if ( !GridNoOnVisibleWorldTile( sNewGridNo ) )
  1996. {
  1997. pSoldier->sGridNo = sNewGridNo;
  1998. return;
  1999. }
  2000. // Alrighty, update UI for this guy, if he's the selected guy...
  2001. if ( gusSelectedSoldier == pSoldier->ubID )
  2002. {
  2003. if ( guiCurrentEvent == C_WAIT_FOR_CONFIRM )
  2004. {
  2005. // Update path!
  2006. gfPlotNewMovement = TRUE;
  2007. }
  2008. }
  2009. // Reset some flags for optimizations..
  2010. pSoldier->sWalkToAttackGridNo = NOWHERE;
  2011. // ATE: Make sure!
  2012. // RemoveMerc( pSoldier->sGridNo, pSoldier, FALSE );
  2013. pSoldier->sGridNo = sNewGridNo;
  2014. // OK, check for special code to close door...
  2015. if ( pSoldier->bEndDoorOpenCode == 2 )
  2016. {
  2017. pSoldier->bEndDoorOpenCode = 0;
  2018. HandleDoorChangeFromGridNo( pSoldier, pSoldier->sEndDoorOpenCodeData, FALSE );
  2019. }
  2020. // OK, Update buddy's strategic insertion code....
  2021. pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
  2022. pSoldier->usStrategicInsertionData = sNewGridNo;
  2023. // Remove this gridno as a reserved place!
  2024. if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) )
  2025. {
  2026. UnMarkMovementReserved( pSoldier );
  2027. }
  2028. if ( pSoldier->sInitialGridNo == 0 )
  2029. {
  2030. pSoldier->sInitialGridNo = sNewGridNo;
  2031. pSoldier->usPatrolGrid[0] = sNewGridNo;
  2032. }
  2033. // Add records of this guy being adjacent
  2034. for (bDir = 0; bDir < NUM_WORLD_DIRECTIONS; bDir++)
  2035. {
  2036. gpWorldLevelData[ pSoldier->sGridNo + DirIncrementer[ bDir ] ].ubAdjacentSoldierCnt++;
  2037. }
  2038. if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) )
  2039. {
  2040. DropSmell( pSoldier );
  2041. }
  2042. // HANDLE ANY SPECIAL RENDERING SITUATIONS
  2043. pSoldier->sZLevelOverride = -1;
  2044. // If we are over a fence ( hopping ), make us higher!
  2045. if ( IsJumpableFencePresentAtGridno( sNewGridNo ) )
  2046. {
  2047. //sX = MapX( sNewGridNo );
  2048. //sY = MapY( sNewGridNo );
  2049. //GetWorldXYAbsoluteScreenXY( sX, sY, &sWorldX, &sZLevel);
  2050. //pSoldier->sZLevelOverride = (sZLevel*Z_SUBLAYERS)+ROOF_Z_LEVEL;
  2051. pSoldier->sZLevelOverride = TOPMOST_Z_LEVEL;
  2052. }
  2053. // Add/ remove tree if we are near it
  2054. // CheckForFullStructures( pSoldier );
  2055. // Add merc at new pos
  2056. if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) )
  2057. {
  2058. AddMercToHead( pSoldier->sGridNo, pSoldier, TRUE );
  2059. // If we are in the middle of climbing the roof!
  2060. if ( pSoldier->usAnimState == CLIMBUPROOF )
  2061. {
  2062. if(pSoldier->iLight!=(-1))
  2063. LightSpriteRoofStatus(pSoldier->iLight, TRUE );
  2064. }
  2065. else if ( pSoldier->usAnimState == CLIMBDOWNROOF )
  2066. {
  2067. if(pSoldier->iLight!=(-1))
  2068. LightSpriteRoofStatus(pSoldier->iLight, FALSE );
  2069. }
  2070. //JA2Gold:
  2071. //if the player wants the merc to cast the fake light AND it is night
  2072. if( pSoldier->bTeam != OUR_TEAM || gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] && NightTime() )
  2073. {
  2074. if ( pSoldier->bLevel > 0 && gpWorldLevelData[pSoldier->sGridNo].pRoofHead != NULL )
  2075. {
  2076. gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubShadeLevel;
  2077. gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubSumLights=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubSumLights;
  2078. gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubMaxLights=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubMaxLights;
  2079. gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubNaturalShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubNaturalShadeLevel;
  2080. }
  2081. else
  2082. {
  2083. gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubShadeLevel;
  2084. gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubSumLights=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubSumLights;
  2085. gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubMaxLights=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubMaxLights;
  2086. gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubNaturalShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubNaturalShadeLevel;
  2087. }
  2088. }
  2089. ///HandlePlacingRoofMarker( pSoldier, pSoldier->sGridNo, TRUE, FALSE );
  2090. HandleAnimationProfile( pSoldier, pSoldier->usAnimState, FALSE );
  2091. HandleCrowShadowNewGridNo( pSoldier );
  2092. }
  2093. pSoldier->bOldOverTerrainType = pSoldier->bOverTerrainType;
  2094. pSoldier->bOverTerrainType = GetTerrainType( pSoldier->sGridNo );
  2095. // OK, check that our animation is up to date!
  2096. // Check our water value
  2097. if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) )
  2098. {
  2099. fInWaterValue = MercInWater( pSoldier );
  2100. // ATE: If ever in water MAKE SURE WE WALK AFTERWOODS!
  2101. if ( fInWaterValue )
  2102. {
  2103. pSoldier->usUIMovementMode = WALKING;
  2104. }
  2105. if ( fInWaterValue != pSoldier->fPrevInWater )
  2106. {
  2107. //Update Animation data
  2108. SetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
  2109. // Update flag
  2110. pSoldier->fPrevInWater = fInWaterValue;
  2111. // Update sound...
  2112. if ( fInWaterValue )
  2113. {
  2114. PlaySoldierJA2Sample( pSoldier->ubID, ENTER_WATER_1, RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ), TRUE );
  2115. }
  2116. else
  2117. {
  2118. // ATE: Check if we are going from water to land - if so, resume
  2119. // with regular movement mode...
  2120. EVENT_InitNewSoldierAnim( pSoldier, pSoldier->usUIMovementMode, 0 , FALSE );
  2121. }
  2122. }
  2123. // OK, If we were not in deep water but we are now, handle deep animations!
  2124. if ( pSoldier->bOverTerrainType == DEEP_WATER && pSoldier->bOldOverTerrainType != DEEP_WATER )
  2125. {
  2126. // Based on our current animation, change!
  2127. switch( pSoldier->usAnimState )
  2128. {
  2129. case WALKING:
  2130. case RUNNING:
  2131. // IN deep water, swim!
  2132. // Make transition from low to deep
  2133. EVENT_InitNewSoldierAnim( pSoldier, LOW_TO_DEEP_WATER, 0 , FALSE );
  2134. pSoldier->usPendingAnimation = DEEP_WATER_SWIM;
  2135. PlayJA2Sample( ENTER_DEEP_WATER_1, RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
  2136. }
  2137. }
  2138. // Damage water if in deep water....
  2139. if ( pSoldier->bOverTerrainType == MED_WATER || pSoldier->bOverTerrainType == DEEP_WATER )
  2140. {
  2141. WaterDamage( pSoldier );
  2142. }
  2143. // OK, If we were in deep water but we are NOT now, handle mid animations!
  2144. if ( pSoldier->bOverTerrainType != DEEP_WATER && pSoldier->bOldOverTerrainType == DEEP_WATER )
  2145. {
  2146. // Make transition from low to deep
  2147. EVENT_InitNewSoldierAnim( pSoldier, DEEP_TO_LOW_WATER, 0 , FALSE );
  2148. pSoldier->usPendingAnimation = pSoldier->usUIMovementMode;
  2149. }
  2150. }
  2151. // are we now standing in tear gas without a decently working gas mask?
  2152. if ( GetSmokeEffectOnTile( sNewGridNo, pSoldier->bLevel ) )
  2153. {
  2154. BOOLEAN fSetGassed = TRUE;
  2155. // If we have a functioning gas mask...
  2156. if ( pSoldier->inv[ HEAD1POS ].usItem == GASMASK && pSoldier->inv[ HEAD1POS ].bStatus[ 0 ] >= GASMASK_MIN_STATUS )
  2157. {
  2158. fSetGassed = FALSE;
  2159. }
  2160. if ( pSoldier->inv[ HEAD2POS ].usItem == GASMASK && pSoldier->inv[ HEAD2POS ].bStatus[ 0 ] >= GASMASK_MIN_STATUS )
  2161. {
  2162. fSetGassed = FALSE;
  2163. }
  2164. if ( fSetGassed )
  2165. {
  2166. pSoldier->uiStatusFlags |= SOLDIER_GASSED;
  2167. }
  2168. }
  2169. if ( pSoldier->bTeam == gbPlayerNum && pSoldier->bStealthMode )
  2170. {
  2171. // Merc got to a new tile by "sneaking". Did we theoretically sneak
  2172. // past an enemy?
  2173. if ( pSoldier->bOppCnt > 0 ) // opponents in sight
  2174. {
  2175. // check each possible enemy
  2176. for ( cnt = 0; cnt < MAX_NUM_SOLDIERS; cnt++ )
  2177. {
  2178. pEnemy = MercPtrs[ cnt ];
  2179. // if this guy is here and alive enough to be looking for us
  2180. if ( pEnemy->bActive && pEnemy->bInSector && ( pEnemy->bLife >= OKLIFE ) )
  2181. {
  2182. // no points for sneaking by the neutrals & friendlies!!!
  2183. if ( !pEnemy->bNeutral && ( pSoldier->bSide != pEnemy->bSide ) && (pEnemy->ubBodyType != COW && pEnemy->ubBodyType != CROW) )
  2184. {
  2185. // if we SEE this particular oppponent, and he DOESN'T see us... and he COULD see us...
  2186. if ( (pSoldier->bOppList[ cnt ] == SEEN_CURRENTLY) &&
  2187. pEnemy->bOppList[ pSoldier->ubID ] != SEEN_CURRENTLY &&
  2188. PythSpacesAway( pSoldier->sGridNo, pEnemy->sGridNo ) < DistanceVisible( pEnemy, DIRECTION_IRRELEVANT, DIRECTION_IRRELEVANT, pSoldier->sGridNo, pSoldier->bLevel ) )
  2189. {
  2190. // AGILITY (5): Soldier snuck 1 square past unaware enemy
  2191. StatChange( pSoldier, AGILAMT, 5, FALSE );
  2192. // Keep looping, we'll give'em 1 point for EACH such enemy!
  2193. }
  2194. }
  2195. }
  2196. }
  2197. }
  2198. }
  2199. // Adjust speed based on terrain, etc
  2200. SetSoldierAniSpeed( pSoldier );
  2201. }
  2202. else
  2203. {
  2204. int i = 0;
  2205. }
  2206. }
  2207. void EVENT_FireSoldierWeapon( SOLDIERTYPE *pSoldier, INT16 sTargetGridNo )
  2208. {
  2209. INT16 sTargetXPos, sTargetYPos;
  2210. BOOLEAN fDoFireRightAway = FALSE;
  2211. // CANNOT BE SAME GRIDNO!
  2212. if ( pSoldier->sGridNo == sTargetGridNo )
  2213. {
  2214. return;
  2215. }
  2216. if ( pSoldier->ubID == 33 )
  2217. {
  2218. int i = 0;
  2219. }
  2220. // Increment the number of people busy doing stuff because of an attack
  2221. //if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) )
  2222. //{
  2223. gTacticalStatus.ubAttackBusyCount++;
  2224. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting attack, attack count now %d", gTacticalStatus.ubAttackBusyCount) );
  2225. //}
  2226. // Set soldier's target gridno
  2227. // This assignment was redundent because it's already set in
  2228. // the actual event call
  2229. pSoldier->sTargetGridNo = sTargetGridNo;
  2230. //pSoldier->sLastTarget = sTargetGridNo;
  2231. pSoldier->ubTargetID = WhoIsThere2( sTargetGridNo, pSoldier->bTargetLevel );
  2232. if (Item[pSoldier->inv[HANDPOS].usItem].usItemClass & IC_GUN)
  2233. {
  2234. if (pSoldier->bDoBurst)
  2235. {
  2236. // Set the TOTAL number of bullets to be fired
  2237. // Can't shoot more bullets than we have in our magazine!
  2238. pSoldier->bBulletsLeft = __min( Weapon[pSoldier->inv[ pSoldier->ubAttackingHand ].usItem].ubShotsPerBurst, pSoldier->inv[ pSoldier->ubAttackingHand ].ubGunShotsLeft );
  2239. }
  2240. else if ( IsValidSecondHandShot( pSoldier ) )
  2241. {
  2242. // two-pistol attack - two bullets!
  2243. pSoldier->bBulletsLeft = 2;
  2244. }
  2245. else
  2246. {
  2247. pSoldier->bBulletsLeft = 1;
  2248. }
  2249. if (pSoldier->inv[ pSoldier->ubAttackingHand ].ubGunAmmoType == AMMO_BUCKSHOT)
  2250. {
  2251. pSoldier->bBulletsLeft *= NUM_BUCKSHOT_PELLETS;
  2252. }
  2253. }
  2254. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting attack, bullets left %d", pSoldier->bBulletsLeft) );
  2255. // Convert our grid-not into an XY
  2256. ConvertGridNoToXY( sTargetGridNo, &sTargetXPos, &sTargetYPos );
  2257. // Change to fire animation
  2258. // Ready weapon
  2259. SoldierReadyWeapon( pSoldier, sTargetXPos, sTargetYPos, FALSE );
  2260. // IF WE ARE AN NPC, SLIDE VIEW TO SHOW WHO IS SHOOTING
  2261. {
  2262. //if ( pSoldier->fDoSpread )
  2263. //{
  2264. // If we are spreading burst, goto right away!
  2265. //EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ), 0, FALSE );
  2266. //}
  2267. // else
  2268. {
  2269. if (pSoldier->uiStatusFlags & SOLDIER_MONSTER )
  2270. {
  2271. // Force our direction!
  2272. EVENT_SetSoldierDirection( pSoldier, pSoldier->bDesiredDirection );
  2273. EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ), 0, FALSE );
  2274. }
  2275. else
  2276. {
  2277. // IF WE ARE IN REAl-TIME, FIRE IMMEDIATELY!
  2278. if ( ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) )
  2279. {
  2280. //fDoFireRightAway = TRUE;
  2281. }
  2282. // Check if our weapon has no intermediate anim...
  2283. switch( pSoldier->inv[ HANDPOS ].usItem )
  2284. {
  2285. case ROCKET_LAUNCHER:
  2286. case MORTAR:
  2287. case GLAUNCHER:
  2288. fDoFireRightAway = TRUE;
  2289. break;
  2290. }
  2291. if ( fDoFireRightAway )
  2292. {
  2293. // Set to true so we don't get toasted twice for APs..
  2294. pSoldier->fDontUnsetLastTargetFromTurn = TRUE;
  2295. // Make sure we don't try and do fancy prone turning.....
  2296. pSoldier->fTurningFromPronePosition = FALSE;
  2297. // Force our direction!
  2298. EVENT_SetSoldierDirection( pSoldier, pSoldier->bDesiredDirection );
  2299. EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ), 0, FALSE );
  2300. }
  2301. else
  2302. {
  2303. // Set flag indicating we are about to shoot once destination direction is hit
  2304. pSoldier->fTurningToShoot = TRUE;
  2305. if ( pSoldier->bTeam != gbPlayerNum && pSoldier->bVisible != -1)
  2306. {
  2307. LocateSoldier( pSoldier->ubID, DONTSETLOCATOR );
  2308. }
  2309. }
  2310. }
  2311. }
  2312. }
  2313. }
  2314. //gAnimControl[ pSoldier->usAnimState ].ubEndHeight
  2315. // ChangeSoldierState( pSoldier, SHOOT_RIFLE_STAND, 0 , FALSE );
  2316. UINT16 SelectFireAnimation( SOLDIERTYPE *pSoldier, UINT8 ubHeight )
  2317. {
  2318. INT16 sDist;
  2319. UINT16 usItem;
  2320. FLOAT dTargetX;
  2321. FLOAT dTargetY;
  2322. FLOAT dTargetZ;
  2323. BOOLEAN fDoLowShot = FALSE;
  2324. //Do different things if we are a monster
  2325. if (pSoldier->uiStatusFlags & SOLDIER_MONSTER)
  2326. {
  2327. switch( pSoldier->ubBodyType )
  2328. {
  2329. case ADULTFEMALEMONSTER:
  2330. case AM_MONSTER:
  2331. case YAF_MONSTER:
  2332. case YAM_MONSTER:
  2333. return( MONSTER_SPIT_ATTACK );
  2334. break;
  2335. case LARVAE_MONSTER:
  2336. break;
  2337. case INFANT_MONSTER:
  2338. return( INFANT_ATTACK );
  2339. break;
  2340. case QUEENMONSTER:
  2341. return( QUEEN_SPIT );
  2342. break;
  2343. }
  2344. return( TRUE );
  2345. }
  2346. if ( pSoldier->ubBodyType == ROBOTNOWEAPON )
  2347. {
  2348. if ( pSoldier->bDoBurst > 0 )
  2349. {
  2350. return( ROBOT_BURST_SHOOT );
  2351. }
  2352. else
  2353. {
  2354. return( ROBOT_SHOOT );
  2355. }
  2356. }
  2357. // Check for rocket laucncher....
  2358. if ( pSoldier->inv[ HANDPOS ].usItem == ROCKET_LAUNCHER )
  2359. {
  2360. return( SHOOT_ROCKET );
  2361. }
  2362. // Check for rocket laucncher....
  2363. if ( pSoldier->inv[ HANDPOS ].usItem == MORTAR )
  2364. {
  2365. return( SHOOT_MORTAR );
  2366. }
  2367. // Check for tank cannon
  2368. if ( pSoldier->inv[ HANDPOS ].usItem == TANK_CANNON )
  2369. {
  2370. return( TANK_SHOOT );
  2371. }
  2372. if ( pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE )
  2373. {
  2374. return( TANK_BURST );
  2375. }
  2376. // Determine which animation to do...depending on stance and gun in hand...
  2377. switch ( ubHeight )
  2378. {
  2379. case ANIM_STAND:
  2380. usItem = pSoldier->inv[ HANDPOS ].usItem;
  2381. // CHECK 2ND HAND!
  2382. if ( IsValidSecondHandShot( pSoldier ) )
  2383. {
  2384. // Increment the number of people busy doing stuff because of an attack
  2385. //gTacticalStatus.ubAttackBusyCount++;
  2386. //DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting attack with 2 guns, attack count now %d", gTacticalStatus.ubAttackBusyCount) );
  2387. return( SHOOT_DUAL_STAND );
  2388. }
  2389. else
  2390. {
  2391. // OK, while standing check distance away from target, and shoot low if we should!
  2392. sDist = PythSpacesAway( pSoldier->sGridNo, pSoldier->sTargetGridNo );
  2393. //ATE: OK, SEE WERE WE ARE TARGETING....
  2394. GetTargetWorldPositions( pSoldier, pSoldier->sTargetGridNo, &dTargetX, &dTargetY, &dTargetZ );
  2395. //CalculateSoldierZPos( pSoldier, FIRING_POS, &dFirerZ );
  2396. if ( sDist <= 2 && dTargetZ <= 100 )
  2397. {
  2398. fDoLowShot = TRUE;
  2399. }
  2400. // ATE: Made distence away long for psitols such that they never use this....
  2401. //if ( !(Item[ usItem ].fFlags & ITEM_TWO_HANDED) )
  2402. //{
  2403. // fDoLowShot = FALSE;
  2404. //}
  2405. // Don't do any low shots if in water
  2406. if ( MercInWater( pSoldier ) )
  2407. {
  2408. fDoLowShot = FALSE;
  2409. }
  2410. if ( pSoldier->bDoBurst > 0 )
  2411. {
  2412. if ( fDoLowShot )
  2413. {
  2414. return( FIRE_BURST_LOW_STAND );
  2415. }
  2416. else
  2417. {
  2418. return( STANDING_BURST );
  2419. }
  2420. }
  2421. else
  2422. {
  2423. if ( fDoLowShot )
  2424. {
  2425. return( FIRE_LOW_STAND );
  2426. }
  2427. else
  2428. {
  2429. return( SHOOT_RIFLE_STAND );
  2430. }
  2431. }
  2432. }
  2433. break;
  2434. case ANIM_PRONE:
  2435. if ( pSoldier->bDoBurst > 0 )
  2436. {
  2437. // pSoldier->fBurstCompleted = FALSE;
  2438. return( PRONE_BURST );
  2439. }
  2440. else
  2441. {
  2442. if ( IsValidSecondHandShot( pSoldier ) )
  2443. {
  2444. return( SHOOT_DUAL_PRONE );
  2445. }
  2446. else
  2447. {
  2448. return( SHOOT_RIFLE_PRONE );
  2449. }
  2450. }
  2451. break;
  2452. case ANIM_CROUCH:
  2453. if ( IsValidSecondHandShot( pSoldier ) )
  2454. {
  2455. // Increment the number of people busy doing stuff because of an attack
  2456. //gTacticalStatus.ubAttackBusyCount++;
  2457. //DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting attack with 2 guns, attack count now %d", gTacticalStatus.ubAttackBusyCount) );
  2458. return( SHOOT_DUAL_CROUCH );
  2459. }
  2460. else
  2461. {
  2462. if ( pSoldier->bDoBurst > 0 )
  2463. {
  2464. // pSoldier->fBurstCompleted = FALSE;
  2465. return( CROUCHED_BURST );
  2466. }
  2467. else
  2468. {
  2469. return( SHOOT_RIFLE_CROUCH );
  2470. }
  2471. }
  2472. break;
  2473. default:
  2474. AssertMsg( FALSE, String( "SelectFireAnimation: ERROR - Invalid height %d", ubHeight ) );
  2475. break;
  2476. }
  2477. // If here, an internal error has occured!
  2478. Assert( FALSE );
  2479. return ( 0 );
  2480. }
  2481. UINT16 GetMoveStateBasedOnStance( SOLDIERTYPE *pSoldier, UINT8 ubStanceHeight )
  2482. {
  2483. // Determine which animation to do...depending on stance and gun in hand...
  2484. switch ( ubStanceHeight )
  2485. {
  2486. case ANIM_STAND:
  2487. if ( pSoldier->fUIMovementFast && !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
  2488. {
  2489. return( RUNNING );
  2490. }
  2491. else
  2492. {
  2493. return( WALKING );
  2494. }
  2495. break;
  2496. case ANIM_PRONE:
  2497. if ( pSoldier->fUIMovementFast )
  2498. {
  2499. return( CRAWLING );
  2500. }
  2501. else
  2502. {
  2503. return( CRAWLING );
  2504. }
  2505. break;
  2506. case ANIM_CROUCH:
  2507. if ( pSoldier->fUIMovementFast )
  2508. {
  2509. return( SWATTING );
  2510. }
  2511. else
  2512. {
  2513. return( SWATTING );
  2514. }
  2515. break;
  2516. default:
  2517. AssertMsg( FALSE, String( "GetMoveStateBasedOnStance: ERROR - Invalid height %d", ubStanceHeight ) );
  2518. break;
  2519. }
  2520. // If here, an internal error has occured!
  2521. Assert( FALSE );
  2522. return ( 0 );
  2523. }
  2524. void SelectFallAnimation( SOLDIERTYPE *pSoldier )
  2525. {
  2526. // Determine which animation to do...depending on stance and gun in hand...
  2527. switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
  2528. {
  2529. case ANIM_STAND:
  2530. EVENT_InitNewSoldierAnim( pSoldier, FLYBACK_HIT, 0 , FALSE );
  2531. break;
  2532. case ANIM_PRONE:
  2533. EVENT_InitNewSoldierAnim( pSoldier, FLYBACK_HIT, 0 , FALSE );
  2534. break;
  2535. }
  2536. }
  2537. BOOLEAN SoldierReadyWeapon( SOLDIERTYPE *pSoldier, INT16 sTargetXPos, INT16 sTargetYPos, BOOLEAN fEndReady )
  2538. {
  2539. INT16 sFacingDir;
  2540. sFacingDir = GetDirectionFromXY( sTargetXPos , sTargetYPos, pSoldier );
  2541. return( InternalSoldierReadyWeapon( pSoldier, (INT8)sFacingDir, fEndReady ) );
  2542. }
  2543. BOOLEAN InternalSoldierReadyWeapon( SOLDIERTYPE *pSoldier, UINT8 sFacingDir, BOOLEAN fEndReady )
  2544. {
  2545. UINT16 usAnimState;
  2546. BOOLEAN fReturnVal = FALSE;
  2547. // Handle monsters differently
  2548. if (pSoldier->uiStatusFlags & SOLDIER_MONSTER)
  2549. {
  2550. if ( !fEndReady )
  2551. {
  2552. EVENT_SetSoldierDesiredDirection( pSoldier, sFacingDir );
  2553. }
  2554. return( FALSE );
  2555. }
  2556. usAnimState = PickSoldierReadyAnimation( pSoldier, fEndReady );
  2557. if ( usAnimState != INVALID_ANIMATION )
  2558. {
  2559. EVENT_InitNewSoldierAnim( pSoldier, usAnimState, 0 , FALSE );
  2560. fReturnVal = TRUE;
  2561. }
  2562. if ( !fEndReady )
  2563. {
  2564. // Ready direction for new facing direction
  2565. if ( usAnimState == INVALID_ANIMATION )
  2566. {
  2567. usAnimState = pSoldier->usAnimState;
  2568. }
  2569. EVENT_InternalSetSoldierDesiredDirection( pSoldier, sFacingDir, FALSE, usAnimState );
  2570. // Check if facing dir is different from ours and change direction if so!
  2571. //if ( sFacingDir != pSoldier->bDirection )
  2572. //{
  2573. // DeductPoints( pSoldier, AP_CHANGE_FACING, 0 );
  2574. //}//
  2575. }
  2576. return( fReturnVal );
  2577. }
  2578. UINT16 PickSoldierReadyAnimation( SOLDIERTYPE *pSoldier, BOOLEAN fEndReady )
  2579. {
  2580. // Invalid animation if nothing in our hands
  2581. if ( pSoldier->inv[ HANDPOS ].usItem == NOTHING )
  2582. {
  2583. return( INVALID_ANIMATION );
  2584. }
  2585. if ( pSoldier->bOverTerrainType == DEEP_WATER )
  2586. {
  2587. return( INVALID_ANIMATION );
  2588. }
  2589. if ( pSoldier->ubBodyType == ROBOTNOWEAPON )
  2590. {
  2591. return( INVALID_ANIMATION );
  2592. }
  2593. // Check if we have a gun.....
  2594. if ( Item[ pSoldier->inv[ HANDPOS ].usItem ].usItemClass != IC_GUN && pSoldier->inv[ HANDPOS ].usItem != GLAUNCHER )
  2595. {
  2596. return( INVALID_ANIMATION );
  2597. }
  2598. if ( pSoldier->inv[ HANDPOS ].usItem == ROCKET_LAUNCHER )
  2599. {
  2600. return( INVALID_ANIMATION );
  2601. }
  2602. if ( pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE )
  2603. {
  2604. return( INVALID_ANIMATION );
  2605. }
  2606. if ( fEndReady )
  2607. {
  2608. // IF our gun is already drawn, do not change animation, just direction
  2609. if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_FIREREADY | ANIM_FIRE ))
  2610. {
  2611. switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
  2612. {
  2613. case ANIM_STAND:
  2614. // CHECK 2ND HAND!
  2615. if ( IsValidSecondHandShot( pSoldier ) )
  2616. {
  2617. return( END_DUAL_STAND );
  2618. }
  2619. else
  2620. {
  2621. return( END_RIFLE_STAND );
  2622. }
  2623. break;
  2624. case ANIM_PRONE:
  2625. if ( IsValidSecondHandShot( pSoldier ) )
  2626. {
  2627. return( END_DUAL_PRONE );
  2628. }
  2629. else
  2630. {
  2631. return( END_RIFLE_PRONE );
  2632. }
  2633. break;
  2634. case ANIM_CROUCH:
  2635. // CHECK 2ND HAND!
  2636. if ( IsValidSecondHandShot( pSoldier ) )
  2637. {
  2638. return( END_DUAL_CROUCH );
  2639. }
  2640. else
  2641. {
  2642. return( END_RIFLE_CROUCH );
  2643. }
  2644. break;
  2645. }
  2646. }
  2647. }
  2648. else
  2649. {
  2650. // IF our gun is already drawn, do not change animation, just direction
  2651. if ( !(gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_FIREREADY | ANIM_FIRE ) ) )
  2652. {
  2653. {
  2654. switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
  2655. {
  2656. case ANIM_STAND:
  2657. // CHECK 2ND HAND!
  2658. if ( IsValidSecondHandShot( pSoldier ) )
  2659. {
  2660. return( READY_DUAL_STAND );
  2661. }
  2662. else
  2663. {
  2664. return( READY_RIFLE_STAND );
  2665. }
  2666. break;
  2667. case ANIM_PRONE:
  2668. // Go into crouch, turn, then go into prone again
  2669. //ChangeSoldierStance( pSoldier, ANIM_CROUCH );
  2670. //pSoldier->ubDesiredHeight = ANIM_PRONE;
  2671. //ChangeSoldierState( pSoldier, PRONE_UP );
  2672. if ( IsValidSecondHandShot( pSoldier ) )
  2673. {
  2674. return( READY_DUAL_PRONE );
  2675. }
  2676. else
  2677. {
  2678. return( READY_RIFLE_PRONE );
  2679. }
  2680. break;
  2681. case ANIM_CROUCH:
  2682. // CHECK 2ND HAND!
  2683. if ( IsValidSecondHandShot( pSoldier ) )
  2684. {
  2685. return( READY_DUAL_CROUCH );
  2686. }
  2687. else
  2688. {
  2689. return( READY_RIFLE_CROUCH );
  2690. }
  2691. break;
  2692. }
  2693. }
  2694. }
  2695. }
  2696. return( INVALID_ANIMATION );
  2697. }
  2698. extern SOLDIERTYPE * FreeUpAttackerGivenTarget( UINT8 ubID, UINT8 ubTargetID );
  2699. extern SOLDIERTYPE * ReduceAttackBusyGivenTarget( UINT8 ubID, UINT8 ubTargetID );
  2700. // ATE: THIS FUNCTION IS USED FOR ALL SOLDIER TAKE DAMAGE FUNCTIONS!
  2701. void EVENT_SoldierGotHit( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, INT16 sBreathLoss, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation, INT16 sSubsequent, INT16 sLocationGrid )
  2702. {
  2703. UINT8 ubCombinedLoss, ubVolume, ubReason;
  2704. SOLDIERTYPE * pNewSoldier;
  2705. ubReason = 0;
  2706. // ATE: If we have gotten hit, but are still in our attack animation, reduce count!
  2707. switch ( pSoldier->usAnimState )
  2708. {
  2709. case SHOOT_ROCKET:
  2710. case SHOOT_MORTAR:
  2711. case THROW_ITEM:
  2712. case LOB_ITEM:
  2713. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker - ATTACK ANIMATION %s ENDED BY HIT ANIMATION, Now %d", gAnimControl[ pSoldier->usAnimState ].zAnimStr, gTacticalStatus.ubAttackBusyCount ) );
  2714. ReduceAttackBusyCount( pSoldier->ubID, FALSE );
  2715. break;
  2716. }
  2717. // DO STUFF COMMON FOR ALL TYPES
  2718. if ( ubAttackerID != NOBODY)
  2719. {
  2720. MercPtrs[ubAttackerID]->bLastAttackHit = TRUE;
  2721. }
  2722. // Set attacker's ID
  2723. pSoldier->ubAttackerID = ubAttackerID;
  2724. if ( !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
  2725. {
  2726. // Increment being attacked count
  2727. pSoldier->bBeingAttackedCount++;
  2728. }
  2729. // if defender is a vehicle, there will be no hit animation played!
  2730. if ( !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
  2731. {
  2732. // Increment the number of people busy doing stuff because of an attack (busy doing hit anim!)
  2733. gTacticalStatus.ubAttackBusyCount++;
  2734. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Person got hit, attack count now %d", gTacticalStatus.ubAttackBusyCount) );
  2735. }
  2736. // ATE; Save hit location info...( for later anim determination stuff )
  2737. pSoldier->ubHitLocation = ubHitLocation;
  2738. // handle morale for heavy damage attacks
  2739. if ( sDamage > 25 )
  2740. {
  2741. if ( pSoldier->ubAttackerID != NOBODY && MercPtrs[ pSoldier->ubAttackerID ]->bTeam == gbPlayerNum )
  2742. {
  2743. HandleMoraleEvent( MercPtrs[ pSoldier->ubAttackerID ], MORALE_DID_LOTS_OF_DAMAGE, MercPtrs[ pSoldier->ubAttackerID ]->sSectorX, MercPtrs[ pSoldier->ubAttackerID ]->sSectorY, MercPtrs[ pSoldier->ubAttackerID ]->bSectorZ );
  2744. }
  2745. if (pSoldier->bTeam == gbPlayerNum)
  2746. {
  2747. HandleMoraleEvent( pSoldier, MORALE_TOOK_LOTS_OF_DAMAGE, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
  2748. }
  2749. }
  2750. // SWITCH IN TYPE OF WEAPON
  2751. if ( ubSpecial == FIRE_WEAPON_TOSSED_OBJECT_SPECIAL )
  2752. {
  2753. ubReason = TAKE_DAMAGE_OBJECT;
  2754. }
  2755. else if ( Item[ usWeaponIndex ].usItemClass & IC_TENTACLES )
  2756. {
  2757. ubReason = TAKE_DAMAGE_TENTACLES;
  2758. }
  2759. else if ( Item[ usWeaponIndex ].usItemClass & ( IC_GUN | IC_THROWING_KNIFE ) )
  2760. {
  2761. if ( ubSpecial == FIRE_WEAPON_SLEEP_DART_SPECIAL )
  2762. {
  2763. UINT32 uiChance;
  2764. // put the drug in!
  2765. pSoldier->bSleepDrugCounter = 10;
  2766. uiChance = SleepDartSuccumbChance( pSoldier );
  2767. if ( PreRandom( 100 ) < uiChance )
  2768. {
  2769. // succumb to the drug!
  2770. sBreathLoss = (INT16)( pSoldier->bBreathMax * 100 );
  2771. }
  2772. }
  2773. else if ( ubSpecial == FIRE_WEAPON_BLINDED_BY_SPIT_SPECIAL )
  2774. {
  2775. // blinded!!
  2776. if ( (pSoldier->bBlindedCounter == 0) )
  2777. {
  2778. // say quote
  2779. if (pSoldier->uiStatusFlags & SOLDIER_PC)
  2780. {
  2781. TacticalCharacterDialogue( pSoldier, QUOTE_BLINDED );
  2782. }
  2783. DecayIndividualOpplist( pSoldier );
  2784. }
  2785. // will always increase counter by at least 1
  2786. pSoldier->bBlindedCounter += (sDamage / 8) + 1;
  2787. // Dirty panel
  2788. fInterfacePanelDirty = DIRTYLEVEL2;
  2789. }
  2790. sBreathLoss += BP_GET_HIT;
  2791. ubReason = TAKE_DAMAGE_GUNFIRE;
  2792. }
  2793. else if ( Item[ usWeaponIndex ].usItemClass & IC_BLADE )
  2794. {
  2795. sBreathLoss = BP_GET_HIT;
  2796. ubReason = TAKE_DAMAGE_BLADE;
  2797. }
  2798. else if ( Item[ usWeaponIndex ].usItemClass & IC_PUNCH )
  2799. {
  2800. // damage from hand-to-hand is 1/4 normal, 3/4 breath.. the sDamage value
  2801. // is actually how much breath we'll take away
  2802. sBreathLoss = sDamage * 100;
  2803. sDamage = sDamage / PUNCH_REAL_DAMAGE_PORTION;
  2804. if ( AreInMeanwhile() && gCurrentMeanwhileDef.ubMeanwhileID == INTERROGATION )
  2805. {
  2806. sBreathLoss = 0;
  2807. sDamage /= 2;
  2808. }
  2809. ubReason = TAKE_DAMAGE_HANDTOHAND;
  2810. }
  2811. else if ( Item[ usWeaponIndex ].usItemClass & IC_EXPLOSV )
  2812. {
  2813. if ( usWeaponIndex == STRUCTURE_EXPLOSION )
  2814. {
  2815. ubReason = TAKE_DAMAGE_STRUCTURE_EXPLOSION;
  2816. }
  2817. else
  2818. {
  2819. ubReason = TAKE_DAMAGE_EXPLOSION;
  2820. }
  2821. }
  2822. else
  2823. {
  2824. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Soldier Control: Weapon class not handled in SoldierGotHit( ) %d", usWeaponIndex ) );
  2825. }
  2826. // CJC: moved to after SoldierTakeDamage so that any quotes from the defender
  2827. // will not be said if they are knocked out or killed
  2828. if ( ubReason != TAKE_DAMAGE_TENTACLES && ubReason != TAKE_DAMAGE_OBJECT )
  2829. {
  2830. // OK, OK: THis is hairy, however, it's ness. because the normal freeup call uses the
  2831. // attckers intended target, and here we want to use thier actual target....
  2832. // ATE: If it's from GUNFIRE damage, keep in mind bullets...
  2833. if ( Item[ usWeaponIndex ].usItemClass & IC_GUN )
  2834. {
  2835. pNewSoldier = FreeUpAttackerGivenTarget( pSoldier->ubAttackerID, pSoldier->ubID );
  2836. }
  2837. else
  2838. {
  2839. pNewSoldier = ReduceAttackBusyGivenTarget( pSoldier->ubAttackerID, pSoldier->ubID );
  2840. }
  2841. if (pNewSoldier != NULL)
  2842. {
  2843. pSoldier = pNewSoldier;
  2844. }
  2845. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Tried to free up attacker, attack count now %d", gTacticalStatus.ubAttackBusyCount) );
  2846. }
  2847. // OK, If we are a vehicle.... damage vehicle...( people inside... )
  2848. if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
  2849. {
  2850. SoldierTakeDamage( pSoldier, ANIM_CROUCH, sDamage, sBreathLoss, ubReason, pSoldier->ubAttackerID, NOWHERE, FALSE, TRUE );
  2851. return;
  2852. }
  2853. // DEDUCT LIFE
  2854. ubCombinedLoss = SoldierTakeDamage( pSoldier, ANIM_CROUCH, sDamage, sBreathLoss, ubReason, pSoldier->ubAttackerID, NOWHERE, FALSE, TRUE );
  2855. // ATE: OK, Let's check our ASSIGNMENT state,
  2856. // If anything other than on a squad or guard, make them guard....
  2857. if ( pSoldier->bTeam == gbPlayerNum )
  2858. {
  2859. if ( pSoldier->bAssignment >= ON_DUTY && pSoldier->bAssignment != ASSIGNMENT_POW )
  2860. {
  2861. if( pSoldier->fMercAsleep )
  2862. {
  2863. pSoldier->fMercAsleep = FALSE;
  2864. pSoldier -> fForcedToStayAwake = FALSE;
  2865. // refresh map screen
  2866. fCharacterInfoPanelDirty = TRUE;
  2867. fTeamPanelDirty = TRUE;
  2868. }
  2869. AddCharacterToAnySquad( pSoldier );
  2870. }
  2871. }
  2872. // SCREAM!!!!
  2873. ubVolume = CalcScreamVolume( pSoldier, ubCombinedLoss );
  2874. // IF WE ARE AT A HIT_STOP ANIMATION
  2875. // DO APPROPRIATE HITWHILE DOWN ANIMATION
  2876. if ( !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_HITSTOP ) || pSoldier->usAnimState != JFK_HITDEATH_STOP )
  2877. {
  2878. MakeNoise( pSoldier->ubID, pSoldier->sGridNo, pSoldier->bLevel, pSoldier->bOverTerrainType, ubVolume, NOISE_SCREAM);
  2879. }
  2880. // IAN ADDED THIS SAT JUNE 14th : HAVE TO SHOW VICTIM!
  2881. if (gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT) && pSoldier->bVisible != -1 && pSoldier->bTeam == gbPlayerNum )
  2882. LocateSoldier(pSoldier->ubID,DONTSETLOCATOR);
  2883. if ( Item[ usWeaponIndex ].usItemClass & IC_BLADE )
  2884. {
  2885. PlayJA2Sample( (UINT32)( KNIFE_IMPACT ), RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
  2886. }
  2887. else
  2888. {
  2889. PlayJA2Sample( (UINT32)( BULLET_IMPACT_1 + Random(3) ), RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
  2890. }
  2891. // PLAY RANDOM GETTING HIT SOUND
  2892. // ONLY IF WE ARE CONSCIOUS!
  2893. if ( pSoldier->bLife >= CONSCIOUSNESS )
  2894. {
  2895. if ( pSoldier->ubBodyType == CROW )
  2896. {
  2897. // Exploding crow...
  2898. PlayJA2Sample( CROW_EXPLODE_1, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
  2899. }
  2900. else
  2901. {
  2902. // ATE: This is to disallow large amounts of smaples being played which is load!
  2903. if ( pSoldier->fGettingHit && pSoldier->usAniCode != STANDING_BURST_HIT )
  2904. {
  2905. }
  2906. else
  2907. {
  2908. DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_HIT1 + Random( 2 ) ) );
  2909. }
  2910. }
  2911. }
  2912. // CHECK FOR DOING HIT WHILE DOWN
  2913. if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_HITSTOP ) )
  2914. {
  2915. switch( pSoldier->usAnimState )
  2916. {
  2917. case FLYBACKHIT_STOP:
  2918. ChangeSoldierState( pSoldier, FALLBACK_DEATHTWICH, 0, FALSE );
  2919. break;
  2920. case STAND_FALLFORWARD_STOP:
  2921. ChangeSoldierState( pSoldier, GENERIC_HIT_DEATHTWITCHNB, 0, FALSE );
  2922. break;
  2923. case JFK_HITDEATH_STOP:
  2924. ChangeSoldierState( pSoldier, JFK_HITDEATH_TWITCHB, 0, FALSE );
  2925. break;
  2926. case FALLBACKHIT_STOP:
  2927. ChangeSoldierState( pSoldier, FALLBACK_HIT_DEATHTWITCHNB, 0, FALSE );
  2928. break;
  2929. case PRONE_LAYFROMHIT_STOP:
  2930. ChangeSoldierState( pSoldier, PRONE_HIT_DEATHTWITCHNB, 0, FALSE );
  2931. break;
  2932. case PRONE_HITDEATH_STOP:
  2933. ChangeSoldierState( pSoldier, PRONE_HIT_DEATHTWITCHB, 0 , FALSE );
  2934. break;
  2935. case FALLFORWARD_HITDEATH_STOP:
  2936. ChangeSoldierState( pSoldier, GENERIC_HIT_DEATHTWITCHB, 0 , FALSE );
  2937. break;
  2938. case FALLBACK_HITDEATH_STOP:
  2939. ChangeSoldierState( pSoldier, FALLBACK_HIT_DEATHTWITCHB, 0 , FALSE );
  2940. break;
  2941. case FALLOFF_DEATH_STOP:
  2942. ChangeSoldierState( pSoldier, FALLOFF_TWITCHB, 0 , FALSE );
  2943. break;
  2944. case FALLOFF_STOP:
  2945. ChangeSoldierState( pSoldier, FALLOFF_TWITCHNB, 0 , FALSE );
  2946. break;
  2947. case FALLOFF_FORWARD_DEATH_STOP:
  2948. ChangeSoldierState( pSoldier, FALLOFF_FORWARD_TWITCHB, 0 , FALSE );
  2949. break;
  2950. case FALLOFF_FORWARD_STOP:
  2951. ChangeSoldierState( pSoldier, FALLOFF_FORWARD_TWITCHNB, 0 , FALSE );
  2952. break;
  2953. default:
  2954. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Soldier Control: Death state %d has no death hit", pSoldier->usAnimState ) );
  2955. }
  2956. return;
  2957. }
  2958. // Set goback to aim after hit flag!
  2959. // Only if we were aiming!
  2960. if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY )
  2961. {
  2962. pSoldier->fGoBackToAimAfterHit = TRUE;
  2963. }
  2964. // IF COWERING, PLAY SPECIFIC GENERIC HIT STAND...
  2965. if ( pSoldier->uiStatusFlags & SOLDIER_COWERING )
  2966. {
  2967. if ( pSoldier->bLife == 0 || IS_MERC_BODY_TYPE( pSoldier ) )
  2968. {
  2969. EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE );
  2970. }
  2971. else
  2972. {
  2973. EVENT_InitNewSoldierAnim( pSoldier, CIV_COWER_HIT, 0 , FALSE );
  2974. }
  2975. return;
  2976. }
  2977. // Change based on body type
  2978. switch( pSoldier->ubBodyType )
  2979. {
  2980. case COW:
  2981. EVENT_InitNewSoldierAnim( pSoldier, COW_HIT, 0 , FALSE );
  2982. return;
  2983. break;
  2984. case BLOODCAT:
  2985. EVENT_InitNewSoldierAnim( pSoldier, BLOODCAT_HIT, 0 , FALSE );
  2986. return;
  2987. break;
  2988. case ADULTFEMALEMONSTER:
  2989. case AM_MONSTER:
  2990. case YAF_MONSTER:
  2991. case YAM_MONSTER:
  2992. EVENT_InitNewSoldierAnim( pSoldier, ADULTMONSTER_HIT, 0 , FALSE );
  2993. return;
  2994. break;
  2995. case LARVAE_MONSTER:
  2996. EVENT_InitNewSoldierAnim( pSoldier, LARVAE_HIT, 0 , FALSE );
  2997. return;
  2998. break;
  2999. case QUEENMONSTER:
  3000. EVENT_InitNewSoldierAnim( pSoldier, QUEEN_HIT, 0 , FALSE );
  3001. return;
  3002. break;
  3003. case CRIPPLECIV:
  3004. {
  3005. // OK, do some code here to allow the fact that poor buddy can be thrown back if it's a big enough hit...
  3006. EVENT_InitNewSoldierAnim( pSoldier, CRIPPLE_HIT, 0 , FALSE );
  3007. //pSoldier->bLife = 0;
  3008. //EVENT_InitNewSoldierAnim( pSoldier, CRIPPLE_DIE_FLYBACK, 0 , FALSE );
  3009. }
  3010. return;
  3011. break;
  3012. case ROBOTNOWEAPON:
  3013. EVENT_InitNewSoldierAnim( pSoldier, ROBOTNW_HIT, 0 , FALSE );
  3014. return;
  3015. break;
  3016. case INFANT_MONSTER:
  3017. EVENT_InitNewSoldierAnim( pSoldier, INFANT_HIT, 0 , FALSE );
  3018. return;
  3019. case CROW:
  3020. EVENT_InitNewSoldierAnim( pSoldier, CROW_DIE, 0 , FALSE );
  3021. return;
  3022. //case FATCIV:
  3023. case MANCIV:
  3024. case MINICIV:
  3025. case DRESSCIV:
  3026. case HATKIDCIV:
  3027. case KIDCIV:
  3028. // OK, if life is 0 and not set as dead ( this is a death hit... )
  3029. if ( !( pSoldier->uiStatusFlags & SOLDIER_DEAD ) && pSoldier->bLife == 0 )
  3030. {
  3031. // Randomize death!
  3032. if ( Random( 2 ) )
  3033. {
  3034. EVENT_InitNewSoldierAnim( pSoldier, CIV_DIE2, 0 , FALSE );
  3035. return;
  3036. }
  3037. }
  3038. // IF here, go generic hit ALWAYS.....
  3039. EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE );
  3040. return;
  3041. break;
  3042. }
  3043. // If here, we are a merc, check if we are in water
  3044. if ( pSoldier->bOverTerrainType == LOW_WATER )
  3045. {
  3046. EVENT_InitNewSoldierAnim( pSoldier, WATER_HIT, 0 , FALSE );
  3047. return;
  3048. }
  3049. if ( pSoldier->bOverTerrainType == DEEP_WATER )
  3050. {
  3051. EVENT_InitNewSoldierAnim( pSoldier, DEEP_WATER_HIT, 0 , FALSE );
  3052. return;
  3053. }
  3054. // SWITCH IN TYPE OF WEAPON
  3055. if ( Item[ usWeaponIndex ].usItemClass & ( IC_GUN | IC_THROWING_KNIFE ) )
  3056. {
  3057. SoldierGotHitGunFire( pSoldier, usWeaponIndex, sDamage, bDirection, sRange, ubAttackerID, ubSpecial, ubHitLocation );
  3058. }
  3059. if ( Item[ usWeaponIndex ].usItemClass & IC_BLADE )
  3060. {
  3061. SoldierGotHitBlade( pSoldier, usWeaponIndex, sDamage, bDirection, sRange, ubAttackerID, ubSpecial, ubHitLocation );
  3062. }
  3063. if ( Item[ usWeaponIndex ].usItemClass & IC_EXPLOSV || Item[ usWeaponIndex ].usItemClass & IC_TENTACLES )
  3064. {
  3065. SoldierGotHitExplosion( pSoldier, usWeaponIndex, sDamage, bDirection, sRange, ubAttackerID, ubSpecial, ubHitLocation );
  3066. }
  3067. if ( Item[ usWeaponIndex ].usItemClass & IC_PUNCH )
  3068. {
  3069. SoldierGotHitPunch( pSoldier, usWeaponIndex, sDamage, bDirection, sRange, ubAttackerID, ubSpecial, ubHitLocation );
  3070. }
  3071. }
  3072. UINT8 CalcScreamVolume( SOLDIERTYPE * pSoldier, UINT8 ubCombinedLoss )
  3073. {
  3074. // NB explosions are so loud they should drown out screams
  3075. UINT8 ubVolume;
  3076. if (ubCombinedLoss < 1)
  3077. {
  3078. ubVolume = 1;
  3079. }
  3080. else
  3081. {
  3082. ubVolume = ubCombinedLoss;
  3083. }
  3084. // Victim yells out in pain, making noise. Yelps are louder from greater
  3085. // wounds, but softer for more experienced soldiers.
  3086. if (ubVolume > (10 - EffectiveExpLevel( pSoldier ) ))
  3087. {
  3088. ubVolume = 10 - EffectiveExpLevel( pSoldier );
  3089. }
  3090. /*
  3091. // the "Speck factor"... He's a whiner, and extra-sensitive to pain!
  3092. if (ptr->trait == NERVOUS)
  3093. ubVolume += 2;
  3094. */
  3095. if (ubVolume < 0)
  3096. {
  3097. ubVolume = 0;
  3098. }
  3099. return( ubVolume );
  3100. }
  3101. void DoGenericHit( SOLDIERTYPE *pSoldier, UINT8 ubSpecial, INT16 bDirection )
  3102. {
  3103. // Based on stance, select generic hit animation
  3104. switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
  3105. {
  3106. case ANIM_STAND:
  3107. // For now, check if we are affected by a burst
  3108. // For now, if the weapon was a gun, special 1 == burst
  3109. // ATE: Only do this for mercs!
  3110. if ( ubSpecial == FIRE_WEAPON_BURST_SPECIAL && pSoldier->ubBodyType <= REGFEMALE )
  3111. {
  3112. //SetSoldierDesiredDirection( pSoldier, bDirection );
  3113. EVENT_SetSoldierDirection( pSoldier, (INT8)bDirection );
  3114. EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->bDirection );
  3115. EVENT_InitNewSoldierAnim( pSoldier, STANDING_BURST_HIT, 0 , FALSE );
  3116. }
  3117. else
  3118. {
  3119. // Check in hand for rifle
  3120. if ( SoldierCarriesTwoHandedWeapon( pSoldier ) )
  3121. {
  3122. EVENT_InitNewSoldierAnim( pSoldier, RIFLE_STAND_HIT, 0 , FALSE );
  3123. }
  3124. else
  3125. {
  3126. EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE );
  3127. }
  3128. }
  3129. break;
  3130. case ANIM_PRONE:
  3131. EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_PRONE, 0 , FALSE );
  3132. break;
  3133. case ANIM_CROUCH:
  3134. EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_CROUCH, 0 , FALSE );
  3135. break;
  3136. }
  3137. }
  3138. void SoldierGotHitGunFire( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation )
  3139. {
  3140. UINT16 usNewGridNo;
  3141. BOOLEAN fBlownAway = FALSE;
  3142. BOOLEAN fHeadHit = FALSE;
  3143. BOOLEAN fFallenOver = FALSE;
  3144. // MAYBE CHANGE TO SPECIAL ANIMATION BASED ON VALUE SET BY DAMAGE CALCULATION CODE
  3145. // ALL THESE ONLY WORK ON STANDING PEOPLE
  3146. if (!(pSoldier->uiStatusFlags & SOLDIER_MONSTER) && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND)
  3147. {
  3148. if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND )
  3149. {
  3150. if (ubSpecial == FIRE_WEAPON_HEAD_EXPLODE_SPECIAL)
  3151. {
  3152. if ( gGameSettings.fOptions[ TOPTION_BLOOD_N_GORE ] )
  3153. {
  3154. if (SpacesAway( pSoldier->sGridNo, Menptr[ubAttackerID].sGridNo ) <= MAX_DISTANCE_FOR_MESSY_DEATH)
  3155. {
  3156. usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (INT8)( DirectionInc( pSoldier->bDirection ) ) );
  3157. // CHECK OK DESTINATION!
  3158. if ( OKFallDirection( pSoldier, usNewGridNo, pSoldier->bLevel, pSoldier->bDirection, JFK_HITDEATH ) )
  3159. {
  3160. usNewGridNo = NewGridNo( (UINT16)usNewGridNo, (INT8)( DirectionInc( pSoldier->bDirection ) ) );
  3161. if ( OKFallDirection( pSoldier, usNewGridNo, pSoldier->bLevel, pSoldier->bDirection, pSoldier->usAnimState ) )
  3162. {
  3163. fHeadHit = TRUE;
  3164. }
  3165. }
  3166. }
  3167. }
  3168. }
  3169. else if (ubSpecial == FIRE_WEAPON_CHEST_EXPLODE_SPECIAL)
  3170. {
  3171. if ( gGameSettings.fOptions[ TOPTION_BLOOD_N_GORE ] )
  3172. {
  3173. if (SpacesAway( pSoldier->sGridNo, Menptr[ubAttackerID].sGridNo ) <= MAX_DISTANCE_FOR_MESSY_DEATH)
  3174. {
  3175. // possibly play torso explosion anim!
  3176. if (pSoldier->bDirection == bDirection)
  3177. {
  3178. usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ pSoldier->bDirection ] ) );
  3179. if ( OKFallDirection( pSoldier, usNewGridNo, pSoldier->bLevel, gOppositeDirection[ bDirection ], FLYBACK_HIT ) )
  3180. {
  3181. usNewGridNo = NewGridNo( (UINT16)usNewGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) );
  3182. if ( OKFallDirection( pSoldier, usNewGridNo, pSoldier->bLevel, gOppositeDirection[ bDirection ], pSoldier->usAnimState ) )
  3183. {
  3184. fBlownAway = TRUE;
  3185. }
  3186. }
  3187. }
  3188. }
  3189. }
  3190. }
  3191. else if (ubSpecial == FIRE_WEAPON_LEG_FALLDOWN_SPECIAL)
  3192. {
  3193. // possibly play fall over anim!
  3194. // this one is NOT restricted by distance
  3195. if (IsValidStance( pSoldier, ANIM_PRONE ) )
  3196. {
  3197. // Can't be in water, or not standing
  3198. if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND && !MercInWater( pSoldier ) )
  3199. {
  3200. fFallenOver = TRUE;
  3201. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzLateLocalizedString[ 20 ], pSoldier->name );
  3202. }
  3203. }
  3204. }
  3205. }
  3206. }
  3207. // IF HERE AND GUY IS DEAD, RETURN!
  3208. if ( pSoldier->uiStatusFlags & SOLDIER_DEAD )
  3209. {
  3210. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Releasesoldierattacker,Dead soldier hit" ) );
  3211. ReleaseSoldiersAttacker( pSoldier );
  3212. return;
  3213. }
  3214. if ( fFallenOver )
  3215. {
  3216. SoldierCollapse( pSoldier );
  3217. return;
  3218. }
  3219. if ( fBlownAway )
  3220. {
  3221. // Only for mercs...
  3222. if ( pSoldier->ubBodyType < 4 )
  3223. {
  3224. ChangeToFlybackAnimation( pSoldier, (INT8)bDirection );
  3225. return;
  3226. }
  3227. }
  3228. if ( fHeadHit )
  3229. {
  3230. // Only for mercs ( or KIDS! )
  3231. if ( pSoldier->ubBodyType < 4 || pSoldier->ubBodyType == HATKIDCIV || pSoldier->ubBodyType == KIDCIV )
  3232. {
  3233. EVENT_InitNewSoldierAnim( pSoldier, JFK_HITDEATH, 0 , FALSE );
  3234. return;
  3235. }
  3236. }
  3237. DoGenericHit( pSoldier, ubSpecial, bDirection );
  3238. }
  3239. void SoldierGotHitExplosion( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation )
  3240. {
  3241. INT16 sNewGridNo;
  3242. // IF HERE AND GUY IS DEAD, RETURN!
  3243. if ( pSoldier->uiStatusFlags & SOLDIER_DEAD )
  3244. {
  3245. return;
  3246. }
  3247. //check for services
  3248. ReceivingSoldierCancelServices( pSoldier );
  3249. GivingSoldierCancelServices( pSoldier );
  3250. if ( gGameSettings.fOptions[ TOPTION_BLOOD_N_GORE ] )
  3251. {
  3252. if ( Explosive[ Item[ usWeaponIndex ].ubClassIndex ].ubRadius >= 3 && pSoldier->bLife == 0 && gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_PRONE )
  3253. {
  3254. if ( sRange >= 2 && sRange <= 4 )
  3255. {
  3256. DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_HIT1 + Random( 2 ) ) );
  3257. EVENT_InitNewSoldierAnim( pSoldier, CHARIOTS_OF_FIRE, 0 , FALSE );
  3258. return;
  3259. }
  3260. else if ( sRange <= 1 )
  3261. {
  3262. DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_HIT1 + Random( 2 ) ) );
  3263. EVENT_InitNewSoldierAnim( pSoldier, BODYEXPLODING, 0 , FALSE );
  3264. return;
  3265. }
  3266. }
  3267. }
  3268. // If we can't fal back or such, so generic hit...
  3269. if ( pSoldier->ubBodyType >= 4 )
  3270. {
  3271. DoGenericHit( pSoldier, 0, bDirection );
  3272. return;
  3273. }
  3274. // Based on stance, select generic hit animation
  3275. switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
  3276. {
  3277. case ANIM_STAND:
  3278. case ANIM_CROUCH:
  3279. EVENT_SetSoldierDirection( pSoldier, (INT8)bDirection );
  3280. EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->bDirection );
  3281. // Check behind us!
  3282. sNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) );
  3283. if ( OKFallDirection( pSoldier, sNewGridNo, pSoldier->bLevel, gOppositeDirection[ bDirection ], FLYBACK_HIT ) )
  3284. {
  3285. ChangeToFallbackAnimation( pSoldier, (INT8)bDirection );
  3286. }
  3287. else
  3288. {
  3289. if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND )
  3290. {
  3291. BeginTyingToFall( pSoldier );
  3292. EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0, FALSE );
  3293. }
  3294. else
  3295. {
  3296. SoldierCollapse( pSoldier );
  3297. }
  3298. }
  3299. break;
  3300. case ANIM_PRONE:
  3301. SoldierCollapse( pSoldier );
  3302. break;
  3303. }
  3304. }
  3305. void SoldierGotHitBlade( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation )
  3306. {
  3307. // IF HERE AND GUY IS DEAD, RETURN!
  3308. if ( pSoldier->uiStatusFlags & SOLDIER_DEAD )
  3309. {
  3310. return;
  3311. }
  3312. // Based on stance, select generic hit animation
  3313. switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
  3314. {
  3315. case ANIM_STAND:
  3316. // Check in hand for rifle
  3317. if ( SoldierCarriesTwoHandedWeapon( pSoldier ) )
  3318. {
  3319. EVENT_InitNewSoldierAnim( pSoldier, RIFLE_STAND_HIT, 0 , FALSE );
  3320. }
  3321. else
  3322. {
  3323. EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE );
  3324. }
  3325. break;
  3326. case ANIM_CROUCH:
  3327. EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_CROUCH, 0 , FALSE );
  3328. break;
  3329. case ANIM_PRONE:
  3330. EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_PRONE, 0 , FALSE );
  3331. break;
  3332. }
  3333. }
  3334. void SoldierGotHitPunch( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation )
  3335. {
  3336. // IF HERE AND GUY IS DEAD, RETURN!
  3337. if ( pSoldier->uiStatusFlags & SOLDIER_DEAD )
  3338. {
  3339. return;
  3340. }
  3341. // Based on stance, select generic hit animation
  3342. switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
  3343. {
  3344. case ANIM_STAND:
  3345. // Check in hand for rifle
  3346. if ( SoldierCarriesTwoHandedWeapon( pSoldier ) )
  3347. {
  3348. EVENT_InitNewSoldierAnim( pSoldier, RIFLE_STAND_HIT, 0 , FALSE );
  3349. }
  3350. else
  3351. {
  3352. EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE );
  3353. }
  3354. break;
  3355. case ANIM_CROUCH:
  3356. EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_CROUCH, 0 , FALSE );
  3357. break;
  3358. case ANIM_PRONE:
  3359. EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_PRONE, 0 , FALSE );
  3360. break;
  3361. }
  3362. }
  3363. BOOLEAN EVENT_InternalGetNewSoldierPath( SOLDIERTYPE *pSoldier, UINT16 sDestGridNo, UINT16 usMovementAnim, BOOLEAN fFromUI, BOOLEAN fForceRestartAnim )
  3364. {
  3365. INT32 iDest;
  3366. INT16 sNewGridNo;
  3367. BOOLEAN fContinue;
  3368. UINT32 uiDist;
  3369. UINT16 usAnimState;
  3370. UINT16 usMoveAnimState = usMovementAnim;
  3371. INT16 sMercGridNo;
  3372. UINT16 usPathingData[ MAX_PATH_LIST_SIZE ];
  3373. UINT8 ubPathingMaxDirection;
  3374. BOOLEAN fAdvancePath = TRUE;
  3375. UINT8 fFlags = 0;
  3376. // Ifd this code, make true if a player
  3377. if ( fFromUI == 3 )
  3378. {
  3379. if ( pSoldier->bTeam == gbPlayerNum )
  3380. {
  3381. fFromUI = 1;
  3382. }
  3383. else
  3384. {
  3385. fFromUI = 0;
  3386. }
  3387. }
  3388. // ATE: if a civ, and from UI, and were cowering, remove from cowering
  3389. if ( AM_AN_EPC( pSoldier ) && fFromUI )
  3390. {
  3391. if ( pSoldier->uiStatusFlags & SOLDIER_COWERING )
  3392. {
  3393. SetSoldierCowerState( pSoldier, FALSE );
  3394. usMoveAnimState = WALKING;
  3395. }
  3396. }
  3397. pSoldier->bGoodContPath = FALSE;
  3398. if ( pSoldier->fDelayedMovement )
  3399. {
  3400. if ( pSoldier->ubDelayedMovementFlags & DELAYED_MOVEMENT_FLAG_PATH_THROUGH_PEOPLE )
  3401. {
  3402. fFlags = PATH_THROUGH_PEOPLE;
  3403. }
  3404. else
  3405. {
  3406. fFlags = PATH_IGNORE_PERSON_AT_DEST;
  3407. }
  3408. pSoldier->fDelayedMovement = FALSE;
  3409. }
  3410. if ( gfGetNewPathThroughPeople )
  3411. {
  3412. fFlags = PATH_THROUGH_PEOPLE;
  3413. }
  3414. // ATE: Some stuff here for realtime, going through interface....
  3415. if ( ( !( gTacticalStatus.uiFlags & INCOMBAT ) && ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) && fFromUI == 1 ) || fFromUI == 2 )
  3416. {
  3417. if ( pSoldier->bCollapsed )
  3418. {
  3419. return( FALSE );
  3420. }
  3421. sMercGridNo = pSoldier->sGridNo;
  3422. pSoldier->sGridNo = pSoldier->sDestination;
  3423. // Check if path is good before copying it into guy's path...
  3424. if ( FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, NO_COPYROUTE, fFlags ) == 0 )
  3425. {
  3426. // Set to old....
  3427. pSoldier->sGridNo = sMercGridNo;
  3428. return( FALSE );
  3429. }
  3430. uiDist = FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, COPYROUTE, fFlags );
  3431. pSoldier->sGridNo = sMercGridNo;
  3432. pSoldier->sFinalDestination = sDestGridNo;
  3433. if ( uiDist > 0 )
  3434. {
  3435. // Add one to path data size....
  3436. if ( fAdvancePath )
  3437. {
  3438. memcpy( usPathingData, pSoldier->usPathingData, sizeof( usPathingData ) );
  3439. ubPathingMaxDirection = (UINT8)usPathingData[ MAX_PATH_LIST_SIZE -1 ];
  3440. memcpy( &(pSoldier->usPathingData[1]), usPathingData, sizeof( usPathingData ) - sizeof( UINT16 ) );
  3441. // If we have reach the max, go back one sFinalDest....
  3442. if ( pSoldier->usPathDataSize == MAX_PATH_LIST_SIZE )
  3443. {
  3444. //pSoldier->sFinalDestination = NewGridNo( (UINT16)pSoldier->sFinalDestination, DirectionInc( gOppositeDirection[ ubPathingMaxDirection ] ) );
  3445. }
  3446. else
  3447. {
  3448. pSoldier->usPathDataSize++;
  3449. }
  3450. }
  3451. usMoveAnimState = pSoldier->usUIMovementMode;
  3452. if ( pSoldier->bOverTerrainType == DEEP_WATER )
  3453. {
  3454. usMoveAnimState = DEEP_WATER_SWIM;
  3455. }
  3456. // Change animation only.... set value to NOT call any goto new gridno stuff.....
  3457. if ( usMoveAnimState != pSoldier->usAnimState )
  3458. {
  3459. //
  3460. pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = TRUE;
  3461. EVENT_InitNewSoldierAnim( pSoldier, usMoveAnimState, 0, FALSE );
  3462. }
  3463. return( TRUE );
  3464. }
  3465. return( FALSE );
  3466. }
  3467. // we can use the soldier's level here because we don't have pathing across levels right now...
  3468. if (pSoldier->bPathStored)
  3469. {
  3470. fContinue = TRUE;
  3471. }
  3472. else
  3473. {
  3474. iDest = FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, usMovementAnim, COPYROUTE, fFlags );
  3475. fContinue = (iDest != 0);
  3476. }
  3477. // Only if we can get a path here
  3478. if ( fContinue )
  3479. {
  3480. // Debug messages
  3481. DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier %d: Get new path", pSoldier->ubID ) );
  3482. // Set final destination
  3483. pSoldier->sFinalDestination = sDestGridNo;
  3484. pSoldier->fPastXDest = 0;
  3485. pSoldier->fPastYDest = 0;
  3486. // CHECK IF FIRST TILE IS FREE
  3487. sNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( (UINT8)pSoldier->usPathingData[ pSoldier->usPathIndex ] ) );
  3488. // If true, we're OK, if not, WAIT for a guy to pass!
  3489. // If we are in deep water, we can only swim!
  3490. if ( pSoldier->bOverTerrainType == DEEP_WATER )
  3491. {
  3492. usMoveAnimState = DEEP_WATER_SWIM;
  3493. }
  3494. // If we were aiming, end aim!
  3495. usAnimState = PickSoldierReadyAnimation( pSoldier, TRUE );
  3496. // Add a pending animation first!
  3497. // Only if we were standing!
  3498. if ( usAnimState != INVALID_ANIMATION && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND )
  3499. {
  3500. EVENT_InitNewSoldierAnim( pSoldier, usAnimState, 0, FALSE );
  3501. pSoldier->usPendingAnimation = usMoveAnimState;
  3502. }
  3503. else
  3504. {
  3505. // Call local copy for change soldier state!
  3506. EVENT_InitNewSoldierAnim( pSoldier, usMoveAnimState, 0, fForceRestartAnim );
  3507. }
  3508. // Change desired direction
  3509. // ATE: Here we have a situation where in RT, we may have
  3510. // gotten a new path, but we are alreayd moving.. so
  3511. // at leasty change new dest. This will be redundent if the ANI is a totaly new one
  3512. return( TRUE );
  3513. }
  3514. return( FALSE );
  3515. }
  3516. void EVENT_GetNewSoldierPath( SOLDIERTYPE *pSoldier, UINT16 sDestGridNo, UINT16 usMovementAnim )
  3517. {
  3518. // ATE: Default restart of animation to TRUE
  3519. EVENT_InternalGetNewSoldierPath( pSoldier, sDestGridNo, usMovementAnim, FALSE, TRUE );
  3520. }
  3521. // Change our state based on stance, to stop!
  3522. void StopSoldier( SOLDIERTYPE *pSoldier )
  3523. {
  3524. ReceivingSoldierCancelServices( pSoldier );
  3525. GivingSoldierCancelServices( pSoldier );
  3526. if ( !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_STATIONARY ) )
  3527. {
  3528. //SoldierGotoStationaryStance( pSoldier );
  3529. EVENT_StopMerc( pSoldier, pSoldier->sGridNo, pSoldier->bDirection );
  3530. }
  3531. // Set desination
  3532. pSoldier->sFinalDestination = pSoldier->sGridNo;
  3533. }
  3534. void SoldierGotoStationaryStance( SOLDIERTYPE *pSoldier )
  3535. {
  3536. // ATE: This is to turn off fast movement, that us used to change movement mode
  3537. // for ui display on stance changes....
  3538. if ( pSoldier->bTeam == gbPlayerNum )
  3539. {
  3540. //pSoldier->fUIMovementFast = FALSE;
  3541. }
  3542. // The queen, if she sees anybody, goes to ready, not normal breath....
  3543. if ( pSoldier->ubBodyType == QUEENMONSTER )
  3544. {
  3545. if ( pSoldier->bOppCnt > 0 || pSoldier->bTeam == gbPlayerNum )
  3546. {
  3547. EVENT_InitNewSoldierAnim( pSoldier, QUEEN_READY, 0 , TRUE );
  3548. return;
  3549. }
  3550. }
  3551. // Check if we are in deep water!
  3552. if ( pSoldier->bOverTerrainType == DEEP_WATER )
  3553. {
  3554. // IN deep water, tred!
  3555. EVENT_InitNewSoldierAnim( pSoldier, DEEP_WATER_TRED, 0 , FALSE );
  3556. }
  3557. else if ( pSoldier->ubServicePartner != NOBODY && pSoldier->bLife >= OKLIFE && pSoldier->bBreath > 0 )
  3558. {
  3559. EVENT_InitNewSoldierAnim( pSoldier, GIVING_AID, 0 , FALSE );
  3560. }
  3561. else
  3562. {
  3563. // Change state back to stationary state for given height
  3564. switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
  3565. {
  3566. case ANIM_STAND:
  3567. // If we are cowering....goto cower state
  3568. if ( pSoldier->uiStatusFlags & SOLDIER_COWERING )
  3569. {
  3570. EVENT_InitNewSoldierAnim( pSoldier, START_COWER, 0 , FALSE );
  3571. }
  3572. else
  3573. {
  3574. EVENT_InitNewSoldierAnim( pSoldier, STANDING, 0 , FALSE );
  3575. }
  3576. break;
  3577. case ANIM_CROUCH:
  3578. // If we are cowering....goto cower state
  3579. if ( pSoldier->uiStatusFlags & SOLDIER_COWERING )
  3580. {
  3581. EVENT_InitNewSoldierAnim( pSoldier, COWERING, 0 , FALSE );
  3582. }
  3583. else
  3584. {
  3585. EVENT_InitNewSoldierAnim( pSoldier, CROUCHING, 0 , FALSE );
  3586. }
  3587. break;
  3588. case ANIM_PRONE:
  3589. EVENT_InitNewSoldierAnim( pSoldier, PRONE, 0 , FALSE );
  3590. break;
  3591. }
  3592. }
  3593. }
  3594. void ChangeSoldierStance( SOLDIERTYPE *pSoldier, UINT8 ubDesiredStance )
  3595. {
  3596. UINT16 usNewState;
  3597. // Check if they are the same!
  3598. if ( ubDesiredStance == gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
  3599. {
  3600. // Free up from stance change
  3601. FreeUpNPCFromStanceChange( pSoldier );
  3602. return;
  3603. }
  3604. // Set UI Busy
  3605. SetUIBusy( pSoldier->ubID );
  3606. // ATE: If we are an NPC, cower....
  3607. if ( pSoldier->ubBodyType >= FATCIV && pSoldier->ubBodyType <= KIDCIV )
  3608. {
  3609. if ( ubDesiredStance == ANIM_STAND )
  3610. {
  3611. SetSoldierCowerState( pSoldier, FALSE );
  3612. }
  3613. else
  3614. {
  3615. SetSoldierCowerState( pSoldier, TRUE );
  3616. }
  3617. }
  3618. else
  3619. {
  3620. usNewState = GetNewSoldierStateFromNewStance( pSoldier, ubDesiredStance );
  3621. // Set desired stance
  3622. pSoldier->ubDesiredHeight = ubDesiredStance;
  3623. // Now change to appropriate animation
  3624. EVENT_InitNewSoldierAnim( pSoldier, usNewState, 0 , FALSE );
  3625. }
  3626. }
  3627. void EVENT_InternalSetSoldierDestination( SOLDIERTYPE *pSoldier, UINT16 usNewDirection, BOOLEAN fFromMove, UINT16 usAnimState )
  3628. {
  3629. UINT16 usNewGridNo;
  3630. INT16 sXPos, sYPos;
  3631. // Get dest gridno, convert to center coords
  3632. usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( usNewDirection ) );
  3633. ConvertMapPosToWorldTileCenter( usNewGridNo, &sXPos, &sYPos );
  3634. // Save new dest gridno, x, y
  3635. pSoldier->sDestination = usNewGridNo;
  3636. pSoldier->sDestXPos = sXPos;
  3637. pSoldier->sDestYPos = sYPos;
  3638. pSoldier->bMovementDirection = (INT8)usNewDirection;
  3639. // OK, ATE: If we are side_stepping, calculate a NEW desired direction....
  3640. if ( pSoldier->bReverse && usAnimState == SIDE_STEP )
  3641. {
  3642. UINT8 ubPerpDirection;
  3643. // Get a new desired direction,
  3644. ubPerpDirection = gPurpendicularDirection[ pSoldier->bDirection ][ usNewDirection ];
  3645. // CHange actual and desired direction....
  3646. EVENT_SetSoldierDirection( pSoldier, ubPerpDirection );
  3647. pSoldier->bDesiredDirection = pSoldier->bDirection;
  3648. }
  3649. else
  3650. {
  3651. if ( !( gAnimControl[ usAnimState ].uiFlags & ANIM_SPECIALMOVE ) )
  3652. {
  3653. EVENT_InternalSetSoldierDesiredDirection( pSoldier, usNewDirection, fFromMove, usAnimState );
  3654. }
  3655. }
  3656. }
  3657. void EVENT_SetSoldierDestination( SOLDIERTYPE *pSoldier, UINT16 usNewDirection )
  3658. {
  3659. EVENT_InternalSetSoldierDestination( pSoldier, usNewDirection, FALSE, pSoldier->usAnimState );
  3660. }
  3661. // function to determine which direction a creature can turn in
  3662. INT8 MultiTiledTurnDirection( SOLDIERTYPE * pSoldier, INT8 bStartDirection, INT8 bDesiredDirection )
  3663. {
  3664. INT8 bTurningIncrement;
  3665. INT8 bCurrentDirection;
  3666. INT8 bLoop;
  3667. UINT16 usStructureID, usAnimSurface;
  3668. STRUCTURE_FILE_REF * pStructureFileRef;
  3669. BOOLEAN fOk = FALSE;
  3670. // start by trying to turn in quickest direction
  3671. bTurningIncrement = (INT8) QuickestDirection( bStartDirection, bDesiredDirection );
  3672. usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, pSoldier->usUIMovementMode );
  3673. pStructureFileRef = GetAnimationStructureRef( pSoldier->ubID, usAnimSurface, pSoldier->usUIMovementMode );
  3674. if ( !pStructureFileRef )
  3675. {
  3676. // without structure data, well, assume quickest direction
  3677. return( bTurningIncrement );
  3678. }
  3679. // ATE: Only if we have a levelnode...
  3680. if ( pSoldier->pLevelNode != NULL && pSoldier->pLevelNode->pStructureData != NULL )
  3681. {
  3682. usStructureID = pSoldier->pLevelNode->pStructureData->usStructureID;
  3683. }
  3684. else
  3685. {
  3686. usStructureID = INVALID_STRUCTURE_ID;
  3687. }
  3688. bLoop = 0;
  3689. bCurrentDirection = bStartDirection;
  3690. while( bLoop < 2 )
  3691. {
  3692. while( bCurrentDirection != bDesiredDirection )
  3693. {
  3694. bCurrentDirection += bTurningIncrement;
  3695. // did we wrap directions?
  3696. if ( bCurrentDirection < 0 )
  3697. {
  3698. bCurrentDirection = (MAXDIR - 1);
  3699. }
  3700. else if ( bCurrentDirection >= MAXDIR )
  3701. {
  3702. bCurrentDirection = 0;
  3703. }
  3704. // check to see if we can add creature in that direction
  3705. fOk = OkayToAddStructureToWorld( pSoldier->sGridNo, pSoldier->bLevel, &(pStructureFileRef->pDBStructureRef[ gOneCDirection[ bCurrentDirection ] ]), usStructureID );
  3706. if (!fOk)
  3707. {
  3708. break;
  3709. }
  3710. }
  3711. if ( (bCurrentDirection == bDesiredDirection) && fOk )
  3712. {
  3713. // success!!
  3714. return( bTurningIncrement );
  3715. }
  3716. bLoop++;
  3717. if ( bLoop < 2 )
  3718. {
  3719. // change direction of loop etc
  3720. bCurrentDirection = bStartDirection;
  3721. bTurningIncrement *= -1;
  3722. }
  3723. }
  3724. // nothing found... doesn't matter much what we return
  3725. return( bTurningIncrement );
  3726. }
  3727. void EVENT_InternalSetSoldierDesiredDirection( SOLDIERTYPE *pSoldier, UINT16 usNewDirection, BOOLEAN fInitalMove, UINT16 usAnimState )
  3728. {
  3729. //if ( usAnimState == WALK_BACKWARDS )
  3730. if ( pSoldier->bReverse && usAnimState != SIDE_STEP )
  3731. {
  3732. // OK, check if we are going to go in the exact opposite than our facing....
  3733. usNewDirection = gOppositeDirection[ usNewDirection ];
  3734. }
  3735. pSoldier->bDesiredDirection = (INT8)usNewDirection;
  3736. // If we are prone, goto crouched first!
  3737. // ONly if we are stationary, and only if directions are differnet!
  3738. // ATE: If we are fNoAPsToFinnishMove, stop what we were doing and
  3739. // reset flag.....
  3740. if ( pSoldier->fNoAPToFinishMove && ( gAnimControl[ usAnimState ].uiFlags & ANIM_MOVING ) )
  3741. {
  3742. // ATE; Commented this out: NEVER, EVER, start a new anim from this function, as an eternal loop will result....
  3743. //SoldierGotoStationaryStance( pSoldier );
  3744. // Reset flag!
  3745. AdjustNoAPToFinishMove( pSoldier, FALSE );
  3746. }
  3747. if ( pSoldier->bDesiredDirection != pSoldier->bDirection )
  3748. {
  3749. if ( gAnimControl[ usAnimState ].uiFlags & ( ANIM_BREATH | ANIM_OK_CHARGE_AP_FOR_TURN | ANIM_FIREREADY ) && !fInitalMove && !pSoldier->fDontChargeTurningAPs )
  3750. {
  3751. // Deduct points for initial turn!
  3752. switch( gAnimControl[ usAnimState ].ubEndHeight )
  3753. {
  3754. // Now change to appropriate animation
  3755. case ANIM_STAND:
  3756. DeductPoints( pSoldier, AP_LOOK_STANDING, 0 );
  3757. break;
  3758. case ANIM_CROUCH:
  3759. DeductPoints( pSoldier, AP_LOOK_CROUCHED, 0 );
  3760. break;
  3761. case ANIM_PRONE:
  3762. DeductPoints( pSoldier, AP_LOOK_PRONE, 0 );
  3763. break;
  3764. }
  3765. }
  3766. pSoldier->fDontChargeTurningAPs = FALSE;
  3767. if ( fInitalMove )
  3768. {
  3769. if ( gAnimControl[ usAnimState ].ubHeight == ANIM_PRONE )
  3770. {
  3771. if ( pSoldier->fTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE )
  3772. {
  3773. pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_START_UP_FROM_MOVE;
  3774. }
  3775. }
  3776. }
  3777. if ( gAnimControl[ usAnimState ].uiFlags & ANIM_STATIONARY || pSoldier->fNoAPToFinishMove || fInitalMove )
  3778. {
  3779. if ( gAnimControl[ usAnimState ].ubHeight == ANIM_PRONE )
  3780. {
  3781. // Set this beasty of a flag to allow us to go back down to prone if we choose!
  3782. // ATE: Alrighty, set flag to go back down only if we are not moving anywhere
  3783. //if ( pSoldier->sDestination == pSoldier->sGridNo )
  3784. if ( !fInitalMove )
  3785. {
  3786. pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_ON;
  3787. // Set a pending animation to change stance first...
  3788. SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH );
  3789. }
  3790. }
  3791. }
  3792. }
  3793. // Set desired direction for the extended directions...
  3794. pSoldier->ubHiResDesiredDirection = ubExtDirection[ pSoldier->bDesiredDirection ];
  3795. if ( pSoldier->bDesiredDirection != pSoldier->bDirection )
  3796. {
  3797. if ( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE ) || CREATURE_OR_BLOODCAT( pSoldier ) )
  3798. {
  3799. pSoldier->uiStatusFlags |= SOLDIER_PAUSEANIMOVE;
  3800. }
  3801. }
  3802. if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
  3803. {
  3804. pSoldier->bTurningIncrement = (INT8) ExtQuickestDirection( pSoldier->ubHiResDirection, pSoldier->ubHiResDesiredDirection );
  3805. }
  3806. else
  3807. {
  3808. if ( pSoldier->uiStatusFlags & SOLDIER_MULTITILE )
  3809. {
  3810. pSoldier->bTurningIncrement = (INT8) MultiTiledTurnDirection( pSoldier, pSoldier->bDirection, pSoldier->bDesiredDirection );
  3811. }
  3812. else
  3813. {
  3814. pSoldier->bTurningIncrement = (INT8) QuickestDirection( pSoldier->bDirection, pSoldier->bDesiredDirection );
  3815. }
  3816. }
  3817. }
  3818. void EVENT_SetSoldierDesiredDirection( SOLDIERTYPE *pSoldier, UINT16 usNewDirection )
  3819. {
  3820. EVENT_InternalSetSoldierDesiredDirection( pSoldier, usNewDirection, FALSE, pSoldier->usAnimState );
  3821. }
  3822. void EVENT_SetSoldierDirection( SOLDIERTYPE *pSoldier, UINT16 usNewDirection )
  3823. {
  3824. // Remove old location data
  3825. HandleAnimationProfile( pSoldier, pSoldier->usAnimState, TRUE );
  3826. pSoldier->bDirection = (INT8)usNewDirection;
  3827. // Updated extended direction.....
  3828. pSoldier->ubHiResDirection = ubExtDirection[ pSoldier->bDirection ];
  3829. // Add new stuff
  3830. HandleAnimationProfile( pSoldier, pSoldier->usAnimState, FALSE );
  3831. // If we are turning, we have chaanged our aim!
  3832. if ( !pSoldier->fDontUnsetLastTargetFromTurn )
  3833. {
  3834. pSoldier->sLastTarget = NOWHERE;
  3835. }
  3836. AdjustForFastTurnAnimation( pSoldier );
  3837. // Update structure info!
  3838. // if ( pSoldier->uiStatusFlags & SOLDIER_MULTITILE )
  3839. {
  3840. UpdateMercStructureInfo( pSoldier );
  3841. }
  3842. // Handle Profile data for hit locations
  3843. HandleAnimationProfile( pSoldier, pSoldier->usAnimState, TRUE );
  3844. HandleCrowShadowNewDirection( pSoldier );
  3845. // Change values!
  3846. SetSoldierLocatorOffsets( pSoldier );
  3847. }
  3848. void EVENT_BeginMercTurn( SOLDIERTYPE *pSoldier, BOOLEAN fFromRealTime, INT32 iRealTimeCounter )
  3849. {
  3850. // NB realtimecounter is not used, always passed in as 0 now!
  3851. INT32 iBlood;
  3852. if (pSoldier->bUnderFire)
  3853. {
  3854. // UnderFire now starts at 2 for "under fire this turn",
  3855. // down to 1 for "under fire last turn", to 0.
  3856. pSoldier->bUnderFire--;
  3857. }
  3858. // ATE: Add decay effect sfor drugs...
  3859. if ( fFromRealTime ) //&& iRealTimeCounter % 300 )
  3860. {
  3861. HandleEndTurnDrugAdjustments( pSoldier );
  3862. }
  3863. else
  3864. {
  3865. HandleEndTurnDrugAdjustments( pSoldier );
  3866. }
  3867. // ATE: Don't bleed if in AUTO BANDAGE!
  3868. if ( !gTacticalStatus.fAutoBandageMode )
  3869. {
  3870. // Blood is not for the weak of heart, or mechanical
  3871. if ( !( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE | SOLDIER_ROBOT ) ) )
  3872. {
  3873. if ( pSoldier->bBleeding || pSoldier->bLife < OKLIFE ) // is he bleeding or dying?
  3874. {
  3875. iBlood = CheckBleeding( pSoldier ); // check if he might lose another life point
  3876. // ATE: Only if in sector!
  3877. if ( pSoldier->bInSector )
  3878. {
  3879. if ( iBlood != NOBLOOD )
  3880. {
  3881. DropBlood( pSoldier, (INT8)iBlood, pSoldier->bVisible );
  3882. }
  3883. }
  3884. }
  3885. }
  3886. }
  3887. // survived bleeding, but is he out of breath?
  3888. if ( pSoldier->bLife && !pSoldier->bBreath && MercInWater( pSoldier ) )
  3889. {
  3890. // Drowning...
  3891. }
  3892. // if he is still alive (didn't bleed to death)
  3893. if ( pSoldier->bLife )
  3894. {
  3895. // reduce the effects of any residual shock from past injuries by half
  3896. pSoldier->bShock /= 2;
  3897. // if this person has heard a noise that hasn't been investigated
  3898. if (pSoldier->sNoiseGridno != NOWHERE)
  3899. {
  3900. if (pSoldier->ubNoiseVolume) // and the noise volume is still positive
  3901. {
  3902. pSoldier->ubNoiseVolume--; // the volume of the noise "decays" by 1 point
  3903. if (!pSoldier->ubNoiseVolume) // if the volume has reached zero
  3904. {
  3905. pSoldier->sNoiseGridno = NOWHERE; // forget about the noise!
  3906. }
  3907. }
  3908. }
  3909. // save unused action points up to a maximum
  3910. /*
  3911. if ((savedPts = pSoldier->bActionPts) > MAX_AP_CARRIED)
  3912. savedPts = MAX_AP_CARRIED;
  3913. */
  3914. if ( pSoldier->uiStatusFlags & SOLDIER_GASSED )
  3915. {
  3916. // then must get a gas mask or leave the gassed area to get over it
  3917. if ( ( pSoldier->inv[ HEAD1POS ].usItem == GASMASK || pSoldier->inv[ HEAD2POS ].usItem == GASMASK ) || !( GetSmokeEffectOnTile( pSoldier->sGridNo, pSoldier->bLevel ) ) )
  3918. {
  3919. // Turn off gassed flag....
  3920. pSoldier->uiStatusFlags &= (~SOLDIER_GASSED );
  3921. }
  3922. }
  3923. if ( pSoldier->bBlindedCounter > 0 )
  3924. {
  3925. pSoldier->bBlindedCounter--;
  3926. if (pSoldier->bBlindedCounter == 0)
  3927. {
  3928. // we can SEE!!!!!
  3929. HandleSight( pSoldier, SIGHT_LOOK );
  3930. // Dirty panel
  3931. fInterfacePanelDirty = DIRTYLEVEL2;
  3932. }
  3933. }
  3934. // ATE: To get around a problem...
  3935. // If an AI guy, and we have 0 life, and are still at higher hieght,
  3936. // Kill them.....
  3937. pSoldier->sWeightCarriedAtTurnStart = (INT16) CalculateCarriedWeight( pSoldier );
  3938. UnusedAPsToBreath( pSoldier );
  3939. // Set flag back to normal, after reaching a certain statge
  3940. if ( pSoldier->bBreath > 80 )
  3941. {
  3942. pSoldier->usQuoteSaidFlags &= ( ~SOLDIER_QUOTE_SAID_LOW_BREATH );
  3943. }
  3944. if ( pSoldier->bBreath > 50 )
  3945. {
  3946. pSoldier->usQuoteSaidFlags &= ( ~SOLDIER_QUOTE_SAID_DROWNING );
  3947. }
  3948. if ( pSoldier->ubTurnsUntilCanSayHeardNoise > 0)
  3949. {
  3950. pSoldier->ubTurnsUntilCanSayHeardNoise--;
  3951. }
  3952. if ( pSoldier->bInSector )
  3953. {
  3954. CheckForBreathCollapse( pSoldier );
  3955. }
  3956. CalcNewActionPoints( pSoldier );
  3957. pSoldier->bTilesMoved = 0;
  3958. if ( pSoldier->bInSector )
  3959. {
  3960. BeginSoldierGetup( pSoldier );
  3961. // CJC Nov 30: handle RT opplist decaying in another function which operates less often
  3962. if ( gTacticalStatus.uiFlags & INCOMBAT )
  3963. {
  3964. VerifyAndDecayOpplist( pSoldier );
  3965. // turn off xray
  3966. if ( pSoldier->uiXRayActivatedTime )
  3967. {
  3968. TurnOffXRayEffects( pSoldier );
  3969. }
  3970. }
  3971. if ( (pSoldier->bTeam == gbPlayerNum) && (pSoldier->ubProfile != NO_PROFILE) )
  3972. {
  3973. switch( gMercProfiles[ pSoldier->ubProfile ].bPersonalityTrait )
  3974. {
  3975. case FEAR_OF_INSECTS:
  3976. if ( MercSeesCreature( pSoldier ) )
  3977. {
  3978. HandleMoraleEvent( pSoldier, MORALE_INSECT_PHOBIC_SEES_CREATURE, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
  3979. if ( !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_PERSONALITY) )
  3980. {
  3981. TacticalCharacterDialogue( pSoldier, QUOTE_PERSONALITY_TRAIT );
  3982. pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_PERSONALITY;
  3983. }
  3984. }
  3985. break;
  3986. case CLAUSTROPHOBIC:
  3987. if ( gbWorldSectorZ > 0 && Random( 6 - gbWorldSectorZ ) == 0 )
  3988. {
  3989. // underground!
  3990. HandleMoraleEvent( pSoldier, MORALE_CLAUSTROPHOBE_UNDERGROUND, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
  3991. if ( !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_PERSONALITY) )
  3992. {
  3993. TacticalCharacterDialogue( pSoldier, QUOTE_PERSONALITY_TRAIT );
  3994. pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_PERSONALITY;
  3995. }
  3996. }
  3997. break;
  3998. case NERVOUS:
  3999. if ( DistanceToClosestFriend( pSoldier ) > NERVOUS_RADIUS )
  4000. {
  4001. // augh!!
  4002. if ( pSoldier->bMorale < 50 )
  4003. {
  4004. HandleMoraleEvent( pSoldier, MORALE_NERVOUS_ALONE, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
  4005. if ( !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_PERSONALITY) )
  4006. {
  4007. TacticalCharacterDialogue( pSoldier, QUOTE_PERSONALITY_TRAIT );
  4008. pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_PERSONALITY;
  4009. }
  4010. }
  4011. }
  4012. else
  4013. {
  4014. if ( pSoldier->bMorale > 45 )
  4015. {
  4016. // turn flag off, so that we say it every two turns
  4017. pSoldier->usQuoteSaidFlags &= ~SOLDIER_QUOTE_SAID_PERSONALITY;
  4018. }
  4019. }
  4020. break;
  4021. }
  4022. }
  4023. }
  4024. // Reset quote flags for under heavy fire and close call!
  4025. pSoldier->usQuoteSaidFlags &= ( ~SOLDIER_QUOTE_SAID_BEING_PUMMELED );
  4026. pSoldier->usQuoteSaidExtFlags &= ( ~SOLDIER_QUOTE_SAID_EXT_CLOSE_CALL );
  4027. pSoldier->bNumHitsThisTurn = 0;
  4028. pSoldier->ubSuppressionPoints = 0;
  4029. pSoldier->fCloseCall = FALSE;
  4030. pSoldier->ubMovementNoiseHeard = 0;
  4031. // If soldier has new APs, reset flags!
  4032. if ( pSoldier->bActionPoints > 0 )
  4033. {
  4034. pSoldier->fUIFirstTimeNOAP = FALSE;
  4035. pSoldier->bMoved = FALSE;
  4036. pSoldier->bPassedLastInterrupt = FALSE;
  4037. }
  4038. }
  4039. }
  4040. // UTILITY FUNCTIONS CALLED BY OVERHEAD.H
  4041. UINT8 gDirectionFrom8to2[ ] = { 0, 0, 1, 1, 0, 1, 1, 0 };
  4042. BOOLEAN ConvertAniCodeToAniFrame( SOLDIERTYPE *pSoldier, UINT16 usAniFrame )
  4043. {
  4044. UINT16 usAnimSurface;
  4045. UINT8 ubTempDir;
  4046. // Given ani code, adjust for facing direction
  4047. // get anim surface and determine # of frames
  4048. usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
  4049. CHECKF( usAnimSurface != INVALID_ANIMATION_SURFACE );
  4050. // COnvert world direction into sprite direction
  4051. ubTempDir = gOneCDirection[ pSoldier->bDirection ];
  4052. //If we are only one frame, ignore what the script is telling us!
  4053. if ( gAnimSurfaceDatabase[ usAnimSurface ].ubFlags & ANIM_DATA_FLAG_NOFRAMES )
  4054. {
  4055. usAniFrame = 0;
  4056. }
  4057. if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 32 )
  4058. {
  4059. ubTempDir = gExtOneCDirection[ pSoldier->ubHiResDirection ];
  4060. }
  4061. // Check # of directions /surface, adjust if ness.
  4062. else if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 4 )
  4063. {
  4064. ubTempDir = ubTempDir / 2;
  4065. }
  4066. // Check # of directions /surface, adjust if ness.
  4067. else if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 1 )
  4068. {
  4069. ubTempDir = 0;
  4070. }
  4071. // Check # of directions /surface, adjust if ness.
  4072. else if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 3 )
  4073. {
  4074. if ( pSoldier->bDirection == NORTHWEST )
  4075. {
  4076. ubTempDir = 1;
  4077. }
  4078. if ( pSoldier->bDirection == WEST )
  4079. {
  4080. ubTempDir = 0;
  4081. }
  4082. if ( pSoldier->bDirection == EAST )
  4083. {
  4084. ubTempDir = 2;
  4085. }
  4086. }
  4087. else if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 2 )
  4088. {
  4089. ubTempDir = gDirectionFrom8to2[ pSoldier->bDirection ];
  4090. }
  4091. pSoldier->usAniFrame = usAniFrame + (UINT16) ( ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumFramesPerDir * ubTempDir ) );
  4092. if ( gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject == NULL )
  4093. {
  4094. pSoldier->usAniFrame = 0;
  4095. return( TRUE );
  4096. }
  4097. if ( pSoldier->usAniFrame >= gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->usNumberOfObjects )
  4098. {
  4099. // Debug msg here....
  4100. // ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"Soldier Animation: Wrong Number of frames per number of objects: %d vs %d, %S", gAnimSurfaceDatabase[ usAnimSurface ].uiNumFramesPerDir, gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->usNumberOfObjects, gAnimControl[ pSoldier->usAnimState ].zAnimStr );
  4101. pSoldier->usAniFrame = 0;
  4102. }
  4103. return( TRUE );
  4104. }
  4105. void TurnSoldier( SOLDIERTYPE *pSoldier)
  4106. {
  4107. INT16 sDirection;
  4108. BOOLEAN fDoDirectionChange = TRUE;
  4109. INT32 cnt;
  4110. // If we are a vehicle... DON'T TURN!
  4111. if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
  4112. {
  4113. if ( pSoldier->ubBodyType != TANK_NW && pSoldier->ubBodyType != TANK_NE )
  4114. {
  4115. return;
  4116. }
  4117. }
  4118. // We handle sight now....
  4119. if ( pSoldier->uiStatusFlags & SOLDIER_LOOK_NEXT_TURNSOLDIER )
  4120. {
  4121. if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_STATIONARY && pSoldier->usAnimState != CLIMBUPROOF && pSoldier->usAnimState != CLIMBDOWNROOF ) )
  4122. {
  4123. // HANDLE SIGHT!
  4124. HandleSight( pSoldier,SIGHT_LOOK | SIGHT_RADIO );
  4125. }
  4126. // Turn off!
  4127. pSoldier->uiStatusFlags &= (~SOLDIER_LOOK_NEXT_TURNSOLDIER );
  4128. HandleSystemNewAISituation( pSoldier, FALSE );
  4129. }
  4130. if ( pSoldier->fTurningToShoot )
  4131. {
  4132. if ( pSoldier->bDirection == pSoldier->bDesiredDirection )
  4133. {
  4134. if ( ( (gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY ) && !pSoldier->fTurningFromPronePosition ) || pSoldier->ubBodyType == ROBOTNOWEAPON || pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE )
  4135. {
  4136. EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ), 0, FALSE );
  4137. pSoldier->fTurningToShoot = FALSE;
  4138. // Save last target gridno!
  4139. //pSoldier->sLastTarget = pSoldier->sTargetGridNo;
  4140. }
  4141. // Else check if we are trying to shoot and once was prone, but am now crouched because we needed to turn...
  4142. else if ( pSoldier->fTurningFromPronePosition )
  4143. {
  4144. if ( IsValidStance( pSoldier, ANIM_PRONE ) )
  4145. {
  4146. SendChangeSoldierStanceEvent( pSoldier, ANIM_PRONE );
  4147. pSoldier->usPendingAnimation = SelectFireAnimation( pSoldier, ANIM_PRONE );
  4148. }
  4149. else
  4150. {
  4151. EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, ANIM_CROUCH ), 0, FALSE );
  4152. }
  4153. pSoldier->fTurningToShoot = FALSE;
  4154. pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
  4155. }
  4156. }
  4157. }
  4158. if ( pSoldier->fTurningToFall )
  4159. {
  4160. if ( pSoldier->bDirection == pSoldier->bDesiredDirection )
  4161. {
  4162. SelectFallAnimation( pSoldier );
  4163. pSoldier->fTurningToFall = FALSE;
  4164. }
  4165. }
  4166. if ( pSoldier->fTurningUntilDone && ( pSoldier->ubPendingStanceChange != NO_PENDING_STANCE ) )
  4167. {
  4168. if ( pSoldier->bDirection == pSoldier->bDesiredDirection )
  4169. {
  4170. SendChangeSoldierStanceEvent( pSoldier, pSoldier->ubPendingStanceChange );
  4171. pSoldier->ubPendingStanceChange = NO_PENDING_STANCE;
  4172. pSoldier->fTurningUntilDone = FALSE;
  4173. }
  4174. }
  4175. if ( pSoldier->fTurningUntilDone && ( pSoldier->usPendingAnimation != NO_PENDING_ANIMATION ) )
  4176. {
  4177. if ( pSoldier->bDirection == pSoldier->bDesiredDirection )
  4178. {
  4179. UINT16 usPendingAnimation;
  4180. usPendingAnimation = pSoldier->usPendingAnimation;
  4181. pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
  4182. EVENT_InitNewSoldierAnim( pSoldier, usPendingAnimation, 0 , FALSE );
  4183. pSoldier->fTurningUntilDone = FALSE;
  4184. }
  4185. }
  4186. // Don't do anything if we are at dest direction!
  4187. if ( pSoldier->bDirection == pSoldier->bDesiredDirection )
  4188. {
  4189. if ( pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE )
  4190. {
  4191. if ( pSoldier->iTuringSoundID != NO_SAMPLE )
  4192. {
  4193. SoundStop( pSoldier->iTuringSoundID );
  4194. pSoldier->iTuringSoundID = NO_SAMPLE;
  4195. PlaySoldierJA2Sample( pSoldier->ubID, TURRET_STOP, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ), TRUE );
  4196. }
  4197. }
  4198. // Turn off!
  4199. pSoldier->uiStatusFlags &= (~SOLDIER_LOOK_NEXT_TURNSOLDIER );
  4200. pSoldier->fDontUnsetLastTargetFromTurn = FALSE;
  4201. // Unset ui busy if from ui
  4202. if ( pSoldier->bTurningFromUI && ( pSoldier->fTurningFromPronePosition != 3 ) && ( pSoldier->fTurningFromPronePosition != 1 ) )
  4203. {
  4204. UnSetUIBusy( pSoldier->ubID );
  4205. pSoldier->bTurningFromUI = FALSE;
  4206. }
  4207. if ( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE ) || CREATURE_OR_BLOODCAT( pSoldier ) )
  4208. {
  4209. pSoldier->uiStatusFlags &= (~SOLDIER_PAUSEANIMOVE);
  4210. }
  4211. FreeUpNPCFromTurning( pSoldier, LOOK);
  4212. // Undo our flag for prone turning...
  4213. // Else check if we are trying to shoot and once was prone, but am now crouched because we needed to turn...
  4214. if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_ON )
  4215. {
  4216. // ATE: Don't do this if we have something in our hands we are going to throw!
  4217. if ( IsValidStance( pSoldier, ANIM_PRONE ) && pSoldier->pTempObject == NULL )
  4218. {
  4219. SendChangeSoldierStanceEvent( pSoldier, ANIM_PRONE );
  4220. }
  4221. pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
  4222. }
  4223. // If a special code, make guy crawl after stance change!
  4224. if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE && pSoldier->usAnimState != PRONE_UP && pSoldier->usAnimState != PRONE_DOWN )
  4225. {
  4226. if ( IsValidStance( pSoldier, ANIM_PRONE ) )
  4227. {
  4228. EVENT_InitNewSoldierAnim( pSoldier, CRAWLING, 0, FALSE );
  4229. }
  4230. }
  4231. if ( pSoldier->uiStatusFlags & SOLDIER_TURNINGFROMHIT )
  4232. {
  4233. if ( pSoldier->fGettingHit == 1 )
  4234. {
  4235. if ( pSoldier->usPendingAnimation != FALLFORWARD_ROOF && pSoldier->usPendingAnimation != FALLOFF && pSoldier->usAnimState != FALLFORWARD_ROOF && pSoldier->usAnimState != FALLOFF )
  4236. {
  4237. // Go back to original direction
  4238. EVENT_SetSoldierDesiredDirection( pSoldier, (INT8)pSoldier->uiPendingActionData1 );
  4239. //SETUP GETTING HIT FLAG TO 2
  4240. pSoldier->fGettingHit = 2;
  4241. }
  4242. else
  4243. {
  4244. pSoldier->uiStatusFlags &= (~SOLDIER_TURNINGFROMHIT );
  4245. }
  4246. }
  4247. else if ( pSoldier->fGettingHit == 2 )
  4248. {
  4249. // Turn off
  4250. pSoldier->uiStatusFlags &= (~SOLDIER_TURNINGFROMHIT );
  4251. // Release attacker
  4252. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Releasesoldierattacker, turning from hit animation ended") );
  4253. ReleaseSoldiersAttacker( pSoldier );
  4254. //FREEUP GETTING HIT FLAG
  4255. pSoldier->fGettingHit = FALSE;
  4256. }
  4257. }
  4258. return;
  4259. }
  4260. // IF WE ARE HERE, WE ARE IN THE PROCESS OF TURNING
  4261. // DOUBLE CHECK TO UNSET fNOAPs...
  4262. if ( pSoldier->fNoAPToFinishMove )
  4263. {
  4264. AdjustNoAPToFinishMove( pSoldier, FALSE );
  4265. }
  4266. // Do something different for vehicles....
  4267. if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
  4268. {
  4269. fDoDirectionChange = FALSE;
  4270. // Get new direction
  4271. /*
  4272. sDirection = pSoldier->ubHiResDirection + ExtQuickestDirection( pSoldier->ubHiResDirection, pSoldier->ubHiResDesiredDirection );
  4273. */
  4274. sDirection = pSoldier->ubHiResDirection + pSoldier->bTurningIncrement;
  4275. if (sDirection > 31)
  4276. {
  4277. sDirection = 0;
  4278. }
  4279. else
  4280. {
  4281. if ( sDirection < 0 )
  4282. {
  4283. sDirection = 31;
  4284. }
  4285. }
  4286. pSoldier->ubHiResDirection = (UINT8)sDirection;
  4287. // Are we at a multiple of a 'cardnal' direction?
  4288. for ( cnt = 0; cnt < 8; cnt++ )
  4289. {
  4290. if ( sDirection == ubExtDirection[ cnt ] )
  4291. {
  4292. fDoDirectionChange = TRUE;
  4293. sDirection = (INT16)cnt;
  4294. break;
  4295. }
  4296. }
  4297. if ( pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE )
  4298. {
  4299. if ( pSoldier->iTuringSoundID == NO_SAMPLE )
  4300. {
  4301. pSoldier->iTuringSoundID = PlaySoldierJA2Sample( pSoldier->ubID, TURRET_MOVE, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 100, SoundDir( pSoldier->sGridNo ), TRUE );
  4302. }
  4303. }
  4304. }
  4305. else
  4306. {
  4307. // Get new direction
  4308. //sDirection = pSoldier->bDirection + QuickestDirection( pSoldier->bDirection, pSoldier->bDesiredDirection );
  4309. sDirection = pSoldier->bDirection + pSoldier->bTurningIncrement;
  4310. if (sDirection > 7)
  4311. {
  4312. sDirection = 0;
  4313. }
  4314. else
  4315. {
  4316. if ( sDirection < 0 )
  4317. {
  4318. sDirection = 7;
  4319. }
  4320. }
  4321. }
  4322. // CHECK FOR A VALID TURN DIRECTION
  4323. // This is needed for prone animations as well as any multi-tiled structs
  4324. if ( fDoDirectionChange )
  4325. {
  4326. if ( OKToAddMercToWorld( pSoldier, (INT8)sDirection ) )
  4327. {
  4328. // Don't do this if we are walkoing off screen...
  4329. if ( gubWaitingForAllMercsToExitCode == WAIT_FOR_MERCS_TO_WALKOFF_SCREEN || gubWaitingForAllMercsToExitCode == WAIT_FOR_MERCS_TO_WALK_TO_GRIDNO )
  4330. {
  4331. }
  4332. else
  4333. {
  4334. // ATE: We should only do this if we are STATIONARY!
  4335. if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_STATIONARY ) )
  4336. {
  4337. pSoldier->uiStatusFlags |= SOLDIER_LOOK_NEXT_TURNSOLDIER;
  4338. }
  4339. // otherwise, it's handled next tile...
  4340. }
  4341. EVENT_SetSoldierDirection( pSoldier, sDirection );
  4342. if ( pSoldier->ubBodyType != LARVAE_MONSTER && !MercInWater( pSoldier ) && pSoldier->bOverTerrainType != DIRT_ROAD && pSoldier->bOverTerrainType != PAVED_ROAD )
  4343. {
  4344. PlaySoldierFootstepSound( pSoldier );
  4345. }
  4346. }
  4347. else
  4348. {
  4349. // Are we prone crawling?
  4350. if ( pSoldier->usAnimState == CRAWLING )
  4351. {
  4352. // OK, we want to getup, turn and go prone again....
  4353. SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH );
  4354. pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE;
  4355. }
  4356. // If we are a creature, or multi-tiled, cancel AI action.....?
  4357. else if ( pSoldier->uiStatusFlags & SOLDIER_MULTITILE )
  4358. {
  4359. pSoldier->bDesiredDirection = pSoldier->bDirection;
  4360. }
  4361. }
  4362. }
  4363. }
  4364. UINT8 gRedGlowR[]=
  4365. {
  4366. 0, // Normal shades
  4367. 25,
  4368. 50,
  4369. 75,
  4370. 100,
  4371. 125,
  4372. 150,
  4373. 175,
  4374. 200,
  4375. 225,
  4376. 0, // For gray palettes
  4377. 25,
  4378. 50,
  4379. 75,
  4380. 100,
  4381. 125,
  4382. 150,
  4383. 175,
  4384. 200,
  4385. 225,
  4386. };
  4387. #if 0
  4388. UINT8 gOrangeGlowR[]=
  4389. {
  4390. 0, // Normal shades
  4391. 20,
  4392. 40,
  4393. 60,
  4394. 80,
  4395. 100,
  4396. 120,
  4397. 140,
  4398. 160,
  4399. 180,
  4400. 0, // For gray palettes
  4401. 20,
  4402. 40,
  4403. 60,
  4404. 80,
  4405. 100,
  4406. 120,
  4407. 140,
  4408. 160,
  4409. 180,
  4410. };
  4411. #endif
  4412. UINT8 gOrangeGlowR[]=
  4413. {
  4414. 0, // Normal shades
  4415. 25,
  4416. 50,
  4417. 75,
  4418. 100,
  4419. 125,
  4420. 150,
  4421. 175,
  4422. 200,
  4423. 225,
  4424. 0, // For gray palettes
  4425. 25,
  4426. 50,
  4427. 75,
  4428. 100,
  4429. 125,
  4430. 150,
  4431. 175,
  4432. 200,
  4433. 225,
  4434. };
  4435. #if 0
  4436. UINT8 gOrangeGlowG[]=
  4437. {
  4438. 0, // Normal shades
  4439. 5,
  4440. 10,
  4441. 25,
  4442. 30,
  4443. 35,
  4444. 40,
  4445. 45,
  4446. 50,
  4447. 55,
  4448. 0, // For gray palettes
  4449. 5,
  4450. 10,
  4451. 25,
  4452. 30,
  4453. 35,
  4454. 40,
  4455. 45,
  4456. 50,
  4457. 55,
  4458. };
  4459. #endif
  4460. UINT8 gOrangeGlowG[]=
  4461. {
  4462. 0, // Normal shades
  4463. 20,
  4464. 40,
  4465. 60,
  4466. 80,
  4467. 100,
  4468. 120,
  4469. 140,
  4470. 160,
  4471. 180,
  4472. 0, // For gray palettes
  4473. 20,
  4474. 40,
  4475. 60,
  4476. 80,
  4477. 100,
  4478. 120,
  4479. 140,
  4480. 160,
  4481. 180,
  4482. };
  4483. BOOLEAN CreateSoldierPalettes( SOLDIERTYPE *pSoldier )
  4484. {
  4485. UINT16 usAnimSurface, usPaletteAnimSurface;
  4486. CHAR8 zColFilename[ 100 ];
  4487. INT32 iWhich;
  4488. INT32 cnt;
  4489. INT8 bBodyTypePalette;
  4490. SGPPaletteEntry Temp8BPPPalette[ 256 ];
  4491. //NT32 uiCount;
  4492. //PPaletteEntry Pal[256];
  4493. if ( pSoldier->p8BPPPalette != NULL )
  4494. {
  4495. MemFree( pSoldier->p8BPPPalette );
  4496. pSoldier->p8BPPPalette = NULL;
  4497. }
  4498. // Allocate mem for new palette
  4499. pSoldier->p8BPPPalette = MemAlloc( sizeof( SGPPaletteEntry ) * 256 );
  4500. memset( pSoldier->p8BPPPalette, 0, sizeof( SGPPaletteEntry ) * 256 );
  4501. CHECKF( pSoldier->p8BPPPalette != NULL );
  4502. // --- TAKE FROM CURRENT ANIMATION HVOBJECT!
  4503. usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
  4504. CHECKF( usAnimSurface != INVALID_ANIMATION_SURFACE );
  4505. if ( ( bBodyTypePalette = GetBodyTypePaletteSubstitutionCode( pSoldier, pSoldier->ubBodyType, zColFilename ) ) == -1 )
  4506. {
  4507. // ATE: here we want to use the breath cycle for the palette.....
  4508. usPaletteAnimSurface = LoadSoldierAnimationSurface( pSoldier, STANDING );
  4509. if ( usPaletteAnimSurface != INVALID_ANIMATION_SURFACE )
  4510. {
  4511. // Use palette from HVOBJECT, then use substitution for pants, etc
  4512. memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[ usPaletteAnimSurface ].hVideoObject->pPaletteEntry, sizeof( pSoldier->p8BPPPalette ) * 256 );
  4513. // Substitute based on head, etc
  4514. SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->HeadPal );
  4515. SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->VestPal );
  4516. SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->PantsPal );
  4517. SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->SkinPal );
  4518. }
  4519. }
  4520. else if ( bBodyTypePalette == 0 )
  4521. {
  4522. // Use palette from hvobject
  4523. memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->pPaletteEntry, sizeof( pSoldier->p8BPPPalette ) * 256 );
  4524. }
  4525. else
  4526. {
  4527. // Use col file
  4528. if ( CreateSGPPaletteFromCOLFile( Temp8BPPPalette, zColFilename ) )
  4529. {
  4530. // Copy into palette
  4531. memcpy( pSoldier->p8BPPPalette, Temp8BPPPalette, sizeof( pSoldier->p8BPPPalette ) * 256 );
  4532. }
  4533. else
  4534. {
  4535. // Use palette from hvobject
  4536. memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->pPaletteEntry, sizeof( pSoldier->p8BPPPalette ) * 256 );
  4537. }
  4538. }
  4539. if ( pSoldier->p16BPPPalette != NULL )
  4540. {
  4541. MemFree( pSoldier->p16BPPPalette );
  4542. pSoldier->p16BPPPalette = NULL;
  4543. }
  4544. // -- BUILD 16BPP Palette from this
  4545. pSoldier->p16BPPPalette = Create16BPPPalette( pSoldier->p8BPPPalette );
  4546. for ( iWhich = 0; iWhich < NUM_SOLDIER_SHADES; iWhich++ )
  4547. {
  4548. if ( pSoldier->pShades[ iWhich ] != NULL )
  4549. {
  4550. MemFree( pSoldier->pShades[ iWhich ] );
  4551. pSoldier->pShades[ iWhich ] = NULL;
  4552. }
  4553. }
  4554. for ( iWhich = 0; iWhich < NUM_SOLDIER_EFFECTSHADES; iWhich++ )
  4555. {
  4556. if ( pSoldier->pEffectShades[ iWhich ] != NULL )
  4557. {
  4558. MemFree( pSoldier->pEffectShades[ iWhich ] );
  4559. pSoldier->pEffectShades[ iWhich ] = NULL;
  4560. }
  4561. }
  4562. for ( iWhich = 0; iWhich < 20; iWhich++ )
  4563. {
  4564. if ( pSoldier->pGlowShades[ iWhich ] != NULL )
  4565. {
  4566. MemFree( pSoldier->pGlowShades[ iWhich ] );
  4567. pSoldier->pGlowShades[ iWhich ] = NULL;
  4568. }
  4569. }
  4570. CreateSoldierPaletteTables( pSoldier, HVOBJECT_GLOW_GREEN );
  4571. // Build a grayscale palette for testing grayout of mercs
  4572. //for(uiCount=0; uiCount < 256; uiCount++)
  4573. //{
  4574. // Pal[uiCount].peRed=(UINT8)(uiCount%128)+128;
  4575. // Pal[uiCount].peGreen=(UINT8)(uiCount%128)+128;
  4576. // Pal[uiCount].peBlue=(UINT8)(uiCount%128)+128;
  4577. //}
  4578. pSoldier->pEffectShades[ 0 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 100, 100, 100, TRUE );
  4579. pSoldier->pEffectShades[ 1 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 100, 150, 100, TRUE );
  4580. // Build shades for glowing visible bad guy
  4581. // First do visible guy
  4582. pSoldier->pGlowShades[ 0 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 255, 255, 255, FALSE );
  4583. for ( cnt = 1; cnt < 10; cnt++ )
  4584. {
  4585. pSoldier->pGlowShades[ cnt ] = CreateEnemyGlow16BPPPalette( pSoldier->p8BPPPalette, gRedGlowR[cnt], 255, FALSE );
  4586. }
  4587. // Now for gray guy...
  4588. pSoldier->pGlowShades[ 10 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 100, 100, 100, TRUE );
  4589. for ( cnt = 11; cnt < 19; cnt++ )
  4590. {
  4591. pSoldier->pGlowShades[ cnt ] = CreateEnemyGreyGlow16BPPPalette( pSoldier->p8BPPPalette, gRedGlowR[cnt], 0, FALSE );
  4592. }
  4593. pSoldier->pGlowShades[ 19 ] = CreateEnemyGreyGlow16BPPPalette( pSoldier->p8BPPPalette, gRedGlowR[18], 0, FALSE );
  4594. // ATE: OK, piggyback on the shades we are not using for 2 colored lighting....
  4595. // ORANGE, VISIBLE GUY
  4596. pSoldier->pShades[ 20 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 255, 255, 255, FALSE );
  4597. for ( cnt = 21; cnt < 30; cnt++ )
  4598. {
  4599. pSoldier->pShades[ cnt ] = CreateEnemyGlow16BPPPalette( pSoldier->p8BPPPalette, gOrangeGlowR[ ( cnt - 20 )], gOrangeGlowG[ ( cnt - 20 ) ], TRUE );
  4600. }
  4601. // ORANGE, GREY GUY
  4602. pSoldier->pShades[ 30 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 100, 100, 100, TRUE );
  4603. for ( cnt = 31; cnt < 39; cnt++ )
  4604. {
  4605. pSoldier->pShades[ cnt ] = CreateEnemyGreyGlow16BPPPalette( pSoldier->p8BPPPalette, gOrangeGlowR[ ( cnt - 20 ) ], gOrangeGlowG[ ( cnt - 20 ) ], TRUE );
  4606. }
  4607. pSoldier->pShades[ 39 ] = CreateEnemyGreyGlow16BPPPalette( pSoldier->p8BPPPalette, gOrangeGlowR[18], gOrangeGlowG[18], TRUE );
  4608. return( TRUE );
  4609. }
  4610. void AdjustAniSpeed( SOLDIERTYPE *pSoldier )
  4611. {
  4612. if ( ( gTacticalStatus.uiFlags & SLOW_ANIMATION ) )
  4613. {
  4614. if ( gTacticalStatus.bRealtimeSpeed == -1 )
  4615. {
  4616. pSoldier->sAniDelay = 10000;
  4617. }
  4618. else
  4619. {
  4620. pSoldier->sAniDelay = pSoldier->sAniDelay * ( 1 * gTacticalStatus.bRealtimeSpeed / 2 );
  4621. }
  4622. }
  4623. RESETTIMECOUNTER( pSoldier->UpdateCounter, pSoldier->sAniDelay );
  4624. }
  4625. void CalculateSoldierAniSpeed( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pStatsSoldier )
  4626. {
  4627. UINT32 uiTerrainDelay;
  4628. UINT32 uiSpeed = 0;
  4629. INT8 bBreathDef, bLifeDef, bAgilDef;
  4630. INT8 bAdditional = 0;
  4631. // for those animations which have a speed of zero, we have to calculate it
  4632. // here. Some animation, such as water-movement, have an ADDITIONAL speed
  4633. switch( pSoldier->usAnimState )
  4634. {
  4635. case PRONE:
  4636. case STANDING:
  4637. pSoldier->sAniDelay = ( pStatsSoldier->bBreath * 2 ) + (100 - pStatsSoldier->bLife );
  4638. // Limit it!
  4639. if ( pSoldier->sAniDelay < 40 )
  4640. {
  4641. pSoldier->sAniDelay = 40;
  4642. }
  4643. AdjustAniSpeed( pSoldier );
  4644. return;
  4645. case CROUCHING:
  4646. pSoldier->sAniDelay = ( pStatsSoldier->bBreath * 2 ) + ( (100 - pStatsSoldier->bLife ) );
  4647. // Limit it!
  4648. if ( pSoldier->sAniDelay < 40 )
  4649. {
  4650. pSoldier->sAniDelay = 40;
  4651. }
  4652. AdjustAniSpeed( pSoldier );
  4653. return;
  4654. case WALKING:
  4655. // Adjust based on body type
  4656. bAdditional = (UINT8)( gubAnimWalkSpeeds[ pStatsSoldier->ubBodyType ].sSpeed );
  4657. if ( bAdditional < 0 )
  4658. bAdditional = 0;
  4659. break;
  4660. case RUNNING:
  4661. // Adjust based on body type
  4662. bAdditional = (UINT8)gubAnimRunSpeeds[ pStatsSoldier->ubBodyType ].sSpeed;
  4663. if ( bAdditional < 0 )
  4664. bAdditional = 0;
  4665. break;
  4666. case SWATTING:
  4667. // Adjust based on body type
  4668. if ( pStatsSoldier->ubBodyType <= REGFEMALE )
  4669. {
  4670. bAdditional = (UINT8)gubAnimSwatSpeeds[ pStatsSoldier->ubBodyType ].sSpeed;
  4671. if ( bAdditional < 0 )
  4672. bAdditional = 0;
  4673. }
  4674. break;
  4675. case CRAWLING:
  4676. // Adjust based on body type
  4677. if ( pStatsSoldier->ubBodyType <= REGFEMALE )
  4678. {
  4679. bAdditional = (UINT8)gubAnimCrawlSpeeds[ pStatsSoldier->ubBodyType ].sSpeed;
  4680. if ( bAdditional < 0 )
  4681. bAdditional = 0;
  4682. }
  4683. break;
  4684. case READY_RIFLE_STAND:
  4685. // Raise rifle based on aim vs non-aim.
  4686. if ( pSoldier->bAimTime == 0 )
  4687. {
  4688. // Quick shot
  4689. pSoldier->sAniDelay = 70;
  4690. }
  4691. else
  4692. {
  4693. pSoldier->sAniDelay = 150;
  4694. }
  4695. AdjustAniSpeed( pSoldier );
  4696. return;
  4697. }
  4698. // figure out movement speed (terrspeed)
  4699. if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING )
  4700. {
  4701. uiSpeed = gsTerrainTypeSpeedModifiers[ pStatsSoldier->bOverTerrainType ];
  4702. uiTerrainDelay = uiSpeed;
  4703. }
  4704. else
  4705. {
  4706. uiTerrainDelay = 40; // standing still
  4707. }
  4708. bBreathDef = 50 - ( pStatsSoldier->bBreath / 2 );
  4709. if ( bBreathDef > 30 )
  4710. bBreathDef = 30;
  4711. bAgilDef = 50 - ( EffectiveAgility( pStatsSoldier ) / 4 );
  4712. bLifeDef = 50 - ( pStatsSoldier->bLife / 2 );
  4713. uiTerrainDelay += ( bLifeDef + bBreathDef + bAgilDef + bAdditional );
  4714. pSoldier->sAniDelay = (INT16)uiTerrainDelay;
  4715. // If a moving animation and w/re on drugs, increase speed....
  4716. if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING )
  4717. {
  4718. if ( GetDrugEffect( pSoldier, DRUG_TYPE_ADRENALINE ) )
  4719. {
  4720. pSoldier->sAniDelay = pSoldier->sAniDelay / 2;
  4721. }
  4722. }
  4723. // MODIFTY NOW BASED ON REAL-TIME, ETC
  4724. // Adjust speed, make twice as fast if in turn-based!
  4725. if ( gTacticalStatus.uiFlags & TURNBASED && ( gTacticalStatus.uiFlags & INCOMBAT ) )
  4726. {
  4727. pSoldier->sAniDelay = pSoldier->sAniDelay / 2;
  4728. }
  4729. // MODIFY IF REALTIME COMBAT
  4730. if ( !( gTacticalStatus.uiFlags & INCOMBAT ) )
  4731. {
  4732. // ATE: If realtime, and stealth mode...
  4733. if ( pStatsSoldier->bStealthMode )
  4734. {
  4735. pSoldier->sAniDelay = (INT16)( pSoldier->sAniDelay * 2 );
  4736. }
  4737. //pSoldier->sAniDelay = pSoldier->sAniDelay * ( 1 * gTacticalStatus.bRealtimeSpeed / 2 );
  4738. }
  4739. }
  4740. void SetSoldierAniSpeed( SOLDIERTYPE *pSoldier )
  4741. {
  4742. SOLDIERTYPE *pStatsSoldier;
  4743. // ATE: If we are an enemy and are not visible......
  4744. // Set speed to 0
  4745. if ( ( gTacticalStatus.uiFlags & TURNBASED && ( gTacticalStatus.uiFlags & INCOMBAT ) ) || gTacticalStatus.fAutoBandageMode )
  4746. {
  4747. if ( ( ( pSoldier->bVisible == -1 && pSoldier->bVisible == pSoldier->bLastRenderVisibleValue ) || gTacticalStatus.fAutoBandageMode ) && pSoldier->usAnimState != MONSTER_UP )
  4748. {
  4749. pSoldier->sAniDelay = 0;
  4750. RESETTIMECOUNTER( pSoldier->UpdateCounter, pSoldier->sAniDelay );
  4751. return;
  4752. }
  4753. }
  4754. // Default stats soldier to same as normal soldier.....
  4755. pStatsSoldier = pSoldier;
  4756. if ( pSoldier->fUseMoverrideMoveSpeed )
  4757. {
  4758. pStatsSoldier = MercPtrs[ pSoldier->bOverrideMoveSpeed ];
  4759. }
  4760. // Only calculate if set to zero
  4761. if ( ( pSoldier->sAniDelay = gAnimControl[ pSoldier->usAnimState ].sSpeed ) == 0 )
  4762. {
  4763. CalculateSoldierAniSpeed( pSoldier, pStatsSoldier );
  4764. }
  4765. AdjustAniSpeed( pSoldier );
  4766. if ( _KeyDown( SPACE ) )
  4767. {
  4768. //pSoldier->sAniDelay = 1000;
  4769. }
  4770. }
  4771. ///////////////////////////////////////////////////////
  4772. //PALETTE REPLACEMENT FUNCTIONS
  4773. ///////////////////////////////////////////////////////
  4774. BOOLEAN LoadPaletteData( )
  4775. {
  4776. HWFILE hFile;
  4777. UINT32 cnt, cnt2;
  4778. hFile = FileOpen( PALETTEFILENAME, FILE_ACCESS_READ, FALSE );
  4779. // Read # of types
  4780. if ( !FileRead( hFile, &guiNumPaletteSubRanges, sizeof( guiNumPaletteSubRanges ), NULL ) )
  4781. {
  4782. return( FALSE );
  4783. }
  4784. // Malloc!
  4785. gpPaletteSubRanges = MemAlloc( sizeof( PaletteSubRangeType ) * guiNumPaletteSubRanges );
  4786. gubpNumReplacementsPerRange = MemAlloc( sizeof( UINT8 ) * guiNumPaletteSubRanges );
  4787. // Read # of types for each!
  4788. for ( cnt = 0; cnt < guiNumPaletteSubRanges; cnt++ )
  4789. {
  4790. if ( !FileRead( hFile, &gubpNumReplacementsPerRange[ cnt ], sizeof( UINT8 ), NULL ) )
  4791. {
  4792. return( FALSE );
  4793. }
  4794. }
  4795. // Loop for each one, read in data
  4796. for ( cnt = 0; cnt < guiNumPaletteSubRanges; cnt++ )
  4797. {
  4798. if ( !FileRead( hFile, &gpPaletteSubRanges[ cnt ].ubStart, sizeof( UINT8 ), NULL ) )
  4799. {
  4800. return( FALSE );
  4801. }
  4802. if ( !FileRead( hFile, &gpPaletteSubRanges[ cnt ].ubEnd, sizeof( UINT8 ), NULL ) )
  4803. {
  4804. return( FALSE );
  4805. }
  4806. }
  4807. // Read # of palettes
  4808. if ( !FileRead( hFile, &guiNumReplacements, sizeof( guiNumReplacements ), NULL ) )
  4809. {
  4810. return( FALSE );
  4811. }
  4812. // Malloc!
  4813. gpPalRep = MemAlloc( sizeof( PaletteReplacementType ) * guiNumReplacements );
  4814. // Read!
  4815. for ( cnt = 0; cnt < guiNumReplacements; cnt++ )
  4816. {
  4817. // type
  4818. if ( !FileRead( hFile, &gpPalRep[ cnt ].ubType, sizeof( gpPalRep[ cnt ].ubType ), NULL ) )
  4819. {
  4820. return( FALSE );
  4821. }
  4822. if ( !FileRead( hFile, &gpPalRep[ cnt ].ID, sizeof( gpPalRep[ cnt ].ID ), NULL ) )
  4823. {
  4824. return( FALSE );
  4825. }
  4826. // # entries
  4827. if ( !FileRead( hFile, &gpPalRep[ cnt ].ubPaletteSize, sizeof( gpPalRep[ cnt ].ubPaletteSize ), NULL ) )
  4828. {
  4829. return( FALSE );
  4830. }
  4831. // Malloc
  4832. gpPalRep[ cnt ].r = MemAlloc( gpPalRep[ cnt ].ubPaletteSize );
  4833. CHECKF( gpPalRep[ cnt ].r != NULL );
  4834. gpPalRep[ cnt ].g = MemAlloc( gpPalRep[ cnt ].ubPaletteSize );
  4835. CHECKF( gpPalRep[ cnt ].g != NULL );
  4836. gpPalRep[ cnt ].b = MemAlloc( gpPalRep[ cnt ].ubPaletteSize );
  4837. CHECKF( gpPalRep[ cnt ].b != NULL );
  4838. for( cnt2 = 0; cnt2 < gpPalRep[ cnt ].ubPaletteSize; cnt2++ )
  4839. {
  4840. if ( !FileRead( hFile, &gpPalRep[ cnt ].r[ cnt2 ], sizeof( UINT8 ), NULL ) )
  4841. {
  4842. return( FALSE );
  4843. }
  4844. if ( !FileRead( hFile, &gpPalRep[ cnt ].g[ cnt2 ], sizeof( UINT8 ), NULL ) )
  4845. {
  4846. return( FALSE );
  4847. }
  4848. if ( !FileRead( hFile, &gpPalRep[ cnt ].b[ cnt2 ], sizeof( UINT8 ), NULL ) )
  4849. {
  4850. return( FALSE );
  4851. }
  4852. }
  4853. }
  4854. FileClose( hFile );
  4855. return( TRUE );
  4856. }
  4857. BOOLEAN SetPaletteReplacement( SGPPaletteEntry *p8BPPPalette, PaletteRepID aPalRep )
  4858. {
  4859. UINT32 cnt2;
  4860. UINT8 ubType;
  4861. UINT8 ubPalIndex;
  4862. CHECKF( GetPaletteRepIndexFromID( aPalRep, &ubPalIndex ) );
  4863. // Get range type
  4864. ubType = gpPalRep[ ubPalIndex ].ubType;
  4865. for ( cnt2 = gpPaletteSubRanges[ ubType ].ubStart; cnt2 <= gpPaletteSubRanges[ ubType ].ubEnd; cnt2++ )
  4866. {
  4867. p8BPPPalette[ cnt2 ].peRed = gpPalRep[ ubPalIndex ].r[ cnt2 - gpPaletteSubRanges[ ubType ].ubStart ];
  4868. p8BPPPalette[ cnt2 ].peGreen = gpPalRep[ ubPalIndex ].g[ cnt2 - gpPaletteSubRanges[ ubType ].ubStart ];
  4869. p8BPPPalette[ cnt2 ].peBlue = gpPalRep[ ubPalIndex ].b[ cnt2 - gpPaletteSubRanges[ ubType ].ubStart ];
  4870. }
  4871. return( TRUE );
  4872. }
  4873. BOOLEAN DeletePaletteData( )
  4874. {
  4875. UINT32 cnt;
  4876. // Free!
  4877. if ( gpPaletteSubRanges != NULL )
  4878. {
  4879. MemFree( gpPaletteSubRanges );
  4880. gpPaletteSubRanges = NULL;
  4881. }
  4882. if ( gubpNumReplacementsPerRange != NULL )
  4883. {
  4884. MemFree( gubpNumReplacementsPerRange );
  4885. gubpNumReplacementsPerRange = NULL;
  4886. }
  4887. for ( cnt = 0; cnt < guiNumReplacements; cnt++ )
  4888. {
  4889. // Free
  4890. if ( gpPalRep[ cnt ].r != NULL )
  4891. {
  4892. MemFree( gpPalRep[ cnt ].r );
  4893. gpPalRep[ cnt ].r = NULL;
  4894. }
  4895. if ( gpPalRep[ cnt ].g != NULL )
  4896. {
  4897. MemFree( gpPalRep[ cnt ].g );
  4898. gpPalRep[ cnt ].g = NULL;
  4899. }
  4900. if ( gpPalRep[ cnt ].b != NULL )
  4901. {
  4902. MemFree( gpPalRep[ cnt ].b );
  4903. gpPalRep[ cnt ].b = NULL;
  4904. }
  4905. }
  4906. // Free
  4907. if ( gpPalRep != NULL )
  4908. {
  4909. MemFree( gpPalRep );
  4910. gpPalRep = NULL;
  4911. }
  4912. return( TRUE );
  4913. }
  4914. BOOLEAN GetPaletteRepIndexFromID( PaletteRepID aPalRep, UINT8 *pubPalIndex )
  4915. {
  4916. UINT32 cnt;
  4917. // Check if type exists
  4918. for ( cnt = 0; cnt < guiNumReplacements; cnt++ )
  4919. {
  4920. if ( COMPARE_PALETTEREP_ID( aPalRep, gpPalRep[ cnt ].ID ) )
  4921. {
  4922. *pubPalIndex = ( UINT8 )cnt;
  4923. return( TRUE );
  4924. }
  4925. }
  4926. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Invalid Palette Replacement ID given");
  4927. return( FALSE );
  4928. }
  4929. UINT16 GetNewSoldierStateFromNewStance( SOLDIERTYPE *pSoldier, UINT8 ubDesiredStance )
  4930. {
  4931. UINT16 usNewState;
  4932. INT8 bCurrentHeight;
  4933. bCurrentHeight = ( ubDesiredStance - gAnimControl[ pSoldier->usAnimState ].ubEndHeight );
  4934. // Now change to appropriate animation
  4935. switch( bCurrentHeight )
  4936. {
  4937. case ANIM_STAND - ANIM_CROUCH:
  4938. usNewState = KNEEL_UP;
  4939. break;
  4940. case ANIM_CROUCH - ANIM_STAND:
  4941. usNewState = KNEEL_DOWN;
  4942. break;
  4943. case ANIM_STAND - ANIM_PRONE:
  4944. usNewState = PRONE_UP;
  4945. break;
  4946. case ANIM_PRONE - ANIM_STAND:
  4947. usNewState = KNEEL_DOWN;
  4948. break;
  4949. case ANIM_CROUCH - ANIM_PRONE:
  4950. usNewState = PRONE_UP;
  4951. break;
  4952. case ANIM_PRONE - ANIM_CROUCH:
  4953. usNewState = PRONE_DOWN;
  4954. break;
  4955. default:
  4956. // Cannot get here unless ub desired stance is bogus
  4957. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "GetNewSoldierStateFromNewStance bogus ubDesiredStance value %d", ubDesiredStance ) );
  4958. usNewState = pSoldier->usAnimState;
  4959. }
  4960. return( usNewState );
  4961. }
  4962. void MoveMercFacingDirection( SOLDIERTYPE *pSoldier, BOOLEAN fReverse, FLOAT dMovementDist )
  4963. {
  4964. FLOAT dAngle = (FLOAT)0;
  4965. // Determine which direction we are in
  4966. switch( pSoldier->bDirection )
  4967. {
  4968. case NORTH:
  4969. dAngle = (FLOAT)( -1 * PI );
  4970. break;
  4971. case NORTHEAST:
  4972. dAngle = (FLOAT)( PI * .75 );
  4973. break;
  4974. case EAST:
  4975. dAngle = (FLOAT)( PI / 2 );
  4976. break;
  4977. case SOUTHEAST:
  4978. dAngle = (FLOAT)( PI / 4 );
  4979. break;
  4980. case SOUTH:
  4981. dAngle = (FLOAT)0;
  4982. break;
  4983. case SOUTHWEST:
  4984. //dAngle = (FLOAT)( PI * -.25 );
  4985. dAngle = (FLOAT)-0.786;
  4986. break;
  4987. case WEST:
  4988. dAngle = (FLOAT) ( PI *-.5 );
  4989. break;
  4990. case NORTHWEST:
  4991. dAngle = (FLOAT) ( PI * -.75 );
  4992. break;
  4993. }
  4994. if ( fReverse )
  4995. {
  4996. dMovementDist = dMovementDist * -1;
  4997. }
  4998. MoveMerc( pSoldier, dMovementDist, dAngle, FALSE );
  4999. }
  5000. void BeginSoldierClimbUpRoof( SOLDIERTYPE *pSoldier )
  5001. {
  5002. INT8 bNewDirection;
  5003. if ( FindHeigherLevel( pSoldier, pSoldier->sGridNo, pSoldier->bDirection, &bNewDirection ) && ( pSoldier->bLevel == 0 ) )
  5004. {
  5005. if ( EnoughPoints( pSoldier, GetAPsToClimbRoof( pSoldier, FALSE ), 0, TRUE ) )
  5006. {
  5007. if (pSoldier->bTeam == gbPlayerNum)
  5008. {
  5009. // OK, SET INTERFACE FIRST
  5010. SetUIBusy( pSoldier->ubID );
  5011. }
  5012. pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bNewDirection ) );
  5013. pSoldier->ubPendingDirection = bNewDirection;
  5014. //pSoldier->usPendingAnimation = CLIMBUPROOF;
  5015. EVENT_InitNewSoldierAnim( pSoldier, CLIMBUPROOF, 0 , FALSE );
  5016. InternalReceivingSoldierCancelServices( pSoldier, FALSE );
  5017. InternalGivingSoldierCancelServices( pSoldier, FALSE );
  5018. }
  5019. }
  5020. }
  5021. void BeginSoldierClimbFence( SOLDIERTYPE *pSoldier )
  5022. {
  5023. INT8 bDirection;
  5024. if ( FindFenceJumpDirection( pSoldier, pSoldier->sGridNo, pSoldier->bDirection, &bDirection ) )
  5025. {
  5026. pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bDirection ) );
  5027. pSoldier->fDontChargeTurningAPs = TRUE;
  5028. EVENT_InternalSetSoldierDesiredDirection( pSoldier, bDirection, FALSE, pSoldier->usAnimState );
  5029. pSoldier->fTurningUntilDone = TRUE;
  5030. // ATE: Reset flag to go back to prone...
  5031. pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
  5032. pSoldier->usPendingAnimation = HOPFENCE;
  5033. }
  5034. }
  5035. UINT32 SleepDartSuccumbChance( SOLDIERTYPE * pSoldier )
  5036. {
  5037. UINT32 uiChance;
  5038. INT8 bEffectiveStrength;
  5039. // figure out base chance of succumbing,
  5040. bEffectiveStrength = EffectiveStrength( pSoldier );
  5041. if (bEffectiveStrength > 90)
  5042. {
  5043. uiChance = 110 - bEffectiveStrength;
  5044. }
  5045. else if (bEffectiveStrength > 80)
  5046. {
  5047. uiChance = 120 - bEffectiveStrength;
  5048. }
  5049. else if (bEffectiveStrength > 70)
  5050. {
  5051. uiChance = 130 - bEffectiveStrength;
  5052. }
  5053. else
  5054. {
  5055. uiChance = 140 - bEffectiveStrength;
  5056. }
  5057. // add in a bonus based on how long it's been since shot... highest chance at the beginning
  5058. uiChance += (10 - pSoldier->bSleepDrugCounter);
  5059. return( uiChance );
  5060. }
  5061. void BeginSoldierGetup( SOLDIERTYPE *pSoldier )
  5062. {
  5063. // RETURN IF WE ARE BEING SERVICED
  5064. if ( pSoldier->ubServiceCount > 0 )
  5065. {
  5066. return;
  5067. }
  5068. // ATE: Don't getup if we are in a meanwhile
  5069. if ( AreInMeanwhile( ) )
  5070. {
  5071. return;
  5072. }
  5073. if ( pSoldier->bCollapsed )
  5074. {
  5075. if ( pSoldier->bLife >= OKLIFE && pSoldier->bBreath >= OKBREATH && (pSoldier->bSleepDrugCounter == 0) )
  5076. {
  5077. // get up you hoser!
  5078. pSoldier->bCollapsed = FALSE;
  5079. pSoldier->bTurnsCollapsed = 0;
  5080. if ( IS_MERC_BODY_TYPE( pSoldier ) )
  5081. {
  5082. switch( pSoldier->usAnimState )
  5083. {
  5084. case FALLOFF_FORWARD_STOP:
  5085. case PRONE_LAYFROMHIT_STOP:
  5086. case STAND_FALLFORWARD_STOP:
  5087. ChangeSoldierStance( pSoldier, ANIM_CROUCH );
  5088. break;
  5089. case FALLBACKHIT_STOP:
  5090. case FALLOFF_STOP:
  5091. case FLYBACKHIT_STOP:
  5092. case FALLBACK_HIT_STAND:
  5093. case FALLOFF:
  5094. case FLYBACK_HIT:
  5095. // ROLL OVER
  5096. EVENT_InitNewSoldierAnim( pSoldier, ROLLOVER, 0 , FALSE );
  5097. break;
  5098. default:
  5099. ChangeSoldierStance( pSoldier, ANIM_CROUCH );
  5100. break;
  5101. }
  5102. }
  5103. else
  5104. {
  5105. EVENT_InitNewSoldierAnim( pSoldier, END_COWER, 0 , FALSE );
  5106. }
  5107. }
  5108. else
  5109. {
  5110. pSoldier->bTurnsCollapsed++;
  5111. if ( (gTacticalStatus.bBoxingState == BOXING) && (pSoldier->uiStatusFlags & SOLDIER_BOXER) )
  5112. {
  5113. if (pSoldier->bTurnsCollapsed > 1)
  5114. {
  5115. // We have a winnah! But it isn't this boxer!
  5116. EndBoxingMatch( pSoldier );
  5117. }
  5118. }
  5119. }
  5120. }
  5121. else if ( pSoldier->bSleepDrugCounter > 0 )
  5122. {
  5123. UINT32 uiChance;
  5124. uiChance = SleepDartSuccumbChance( pSoldier );
  5125. if ( PreRandom( 100 ) < uiChance )
  5126. {
  5127. // succumb to the drug!
  5128. DeductPoints( pSoldier, 0, (INT16)( pSoldier->bBreathMax * 100 ) );
  5129. SoldierCollapse( pSoldier );
  5130. }
  5131. }
  5132. if ( pSoldier->bSleepDrugCounter > 0 )
  5133. {
  5134. pSoldier->bSleepDrugCounter--;
  5135. }
  5136. }
  5137. void HandleTakeDamageDeath( SOLDIERTYPE *pSoldier, UINT8 bOldLife, UINT8 ubReason )
  5138. {
  5139. switch( ubReason )
  5140. {
  5141. case TAKE_DAMAGE_BLOODLOSS:
  5142. case TAKE_DAMAGE_ELECTRICITY:
  5143. case TAKE_DAMAGE_GAS:
  5144. if ( pSoldier->bInSector )
  5145. {
  5146. if ( pSoldier->bVisible != -1 )
  5147. {
  5148. if ( ubReason != TAKE_DAMAGE_BLOODLOSS )
  5149. {
  5150. DoMercBattleSound( pSoldier, BATTLE_SOUND_DIE1 );
  5151. pSoldier->fDeadSoundPlayed = TRUE;
  5152. }
  5153. }
  5154. if ( ( ubReason == TAKE_DAMAGE_ELECTRICITY ) && pSoldier->bLife < OKLIFE )
  5155. {
  5156. pSoldier->fInNonintAnim = FALSE;
  5157. }
  5158. // Check for < OKLIFE
  5159. if ( pSoldier->bLife < OKLIFE && pSoldier->bLife != 0 && !pSoldier->bCollapsed)
  5160. {
  5161. SoldierCollapse( pSoldier );
  5162. }
  5163. // THis is for the die animation that will be happening....
  5164. if ( pSoldier->bLife == 0 )
  5165. {
  5166. pSoldier->fDoingExternalDeath = TRUE;
  5167. }
  5168. // Check if he is dead....
  5169. CheckForAndHandleSoldierDyingNotFromHit( pSoldier );
  5170. }
  5171. //if( !( guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) )
  5172. {
  5173. HandleSoldierTakeDamageFeedback( pSoldier );
  5174. }
  5175. if(( guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) || !pSoldier->bInSector )
  5176. {
  5177. if ( pSoldier->bLife == 0 && !( pSoldier->uiStatusFlags & SOLDIER_DEAD ) )
  5178. {
  5179. StrategicHandlePlayerTeamMercDeath( pSoldier );
  5180. // ATE: Here, force always to use die sound...
  5181. pSoldier->fDieSoundUsed = FALSE;
  5182. DoMercBattleSound( pSoldier, BATTLE_SOUND_DIE1 );
  5183. pSoldier->fDeadSoundPlayed = TRUE;
  5184. // ATE: DO death sound
  5185. PlayJA2Sample( (UINT8)DOORCR_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
  5186. PlayJA2Sample( (UINT8)HEADCR_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
  5187. }
  5188. }
  5189. break;
  5190. }
  5191. if ( ubReason == TAKE_DAMAGE_ELECTRICITY )
  5192. {
  5193. if ( pSoldier->bLife >= OKLIFE )
  5194. {
  5195. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Freeing up attacker from electricity damage") );
  5196. ReleaseSoldiersAttacker( pSoldier );
  5197. }
  5198. }
  5199. }
  5200. UINT8 SoldierTakeDamage( SOLDIERTYPE *pSoldier, INT8 bHeight, INT16 sLifeDeduct, INT16 sBreathLoss, UINT8 ubReason, UINT8 ubAttacker, INT16 sSourceGrid, INT16 sSubsequent, BOOLEAN fShowDamage )
  5201. {
  5202. INT8 bOldLife;
  5203. UINT8 ubCombinedLoss;
  5204. INT8 bBandage;
  5205. INT16 sAPCost;
  5206. UINT8 ubBlood;
  5207. pSoldier->ubLastDamageReason = ubReason;
  5208. // CJC Jan 21 99: add check to see if we are hurting an enemy in an enemy-controlled
  5209. // sector; if so, this is a sign of player activity
  5210. switch ( pSoldier->bTeam )
  5211. {
  5212. case ENEMY_TEAM:
  5213. // if we're in the wilderness this always counts
  5214. if ( StrategicMap[ CALCULATE_STRATEGIC_INDEX( gWorldSectorX, gWorldSectorY ) ].fEnemyControlled || SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ].ubTraversability[ THROUGH_STRATEGIC_MOVE ] != TOWN )
  5215. {
  5216. // update current day of activity!
  5217. UpdateLastDayOfPlayerActivity( (UINT16) GetWorldDay() );
  5218. }
  5219. break;
  5220. case CREATURE_TEAM:
  5221. // always a sign of activity?
  5222. UpdateLastDayOfPlayerActivity( (UINT16) GetWorldDay() );
  5223. break;
  5224. case CIV_TEAM:
  5225. if ( pSoldier->ubCivilianGroup == KINGPIN_CIV_GROUP && gubQuest[ QUEST_RESCUE_MARIA ] == QUESTINPROGRESS && gTacticalStatus.bBoxingState == NOT_BOXING )
  5226. {
  5227. SOLDIERTYPE * pMaria = FindSoldierByProfileID( MARIA, FALSE );
  5228. if ( pMaria && pMaria->bActive && pMaria->bInSector )
  5229. {
  5230. SetFactTrue( FACT_MARIA_ESCAPE_NOTICED );
  5231. }
  5232. }
  5233. break;
  5234. default:
  5235. break;
  5236. }
  5237. // Deduct life!, Show damage if we want!
  5238. bOldLife = pSoldier->bLife;
  5239. // OK, If we are a vehicle.... damage vehicle...( people inside... )
  5240. if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
  5241. {
  5242. if ( TANK( pSoldier ) )
  5243. {
  5244. //sLifeDeduct = (sLifeDeduct * 2) / 3;
  5245. }
  5246. else
  5247. {
  5248. if ( ubReason == TAKE_DAMAGE_GUNFIRE )
  5249. {
  5250. sLifeDeduct /= 3;
  5251. }
  5252. else if ( ubReason == TAKE_DAMAGE_EXPLOSION && sLifeDeduct > 50 )
  5253. {
  5254. // boom!
  5255. sLifeDeduct *= 2;
  5256. }
  5257. }
  5258. VehicleTakeDamage( pSoldier->bVehicleID, ubReason, sLifeDeduct, pSoldier->sGridNo, ubAttacker );
  5259. HandleTakeDamageDeath( pSoldier, bOldLife, ubReason );
  5260. return( 0 );
  5261. }
  5262. // ATE: If we are elloit being attacked in a meanwhile...
  5263. if ( pSoldier->uiStatusFlags & SOLDIER_NPC_SHOOTING )
  5264. {
  5265. // Almost kill but not quite.....
  5266. sLifeDeduct = ( pSoldier->bLife - 1 );
  5267. // Turn off
  5268. pSoldier->uiStatusFlags &= ( ~SOLDIER_NPC_SHOOTING );
  5269. }
  5270. // CJC: make sure Elliot doesn't bleed to death!
  5271. if ( ubReason == TAKE_DAMAGE_BLOODLOSS && AreInMeanwhile() )
  5272. {
  5273. return( 0 );
  5274. }
  5275. // Calculate bandage
  5276. bBandage = pSoldier->bLifeMax - pSoldier->bLife - pSoldier->bBleeding;
  5277. if( guiCurrentScreen == MAP_SCREEN )
  5278. {
  5279. fReDrawFace = TRUE;
  5280. }
  5281. if ( CREATURE_OR_BLOODCAT( pSoldier ) )
  5282. {
  5283. INT16 sReductionFactor = 0;
  5284. if ( pSoldier->ubBodyType == BLOODCAT )
  5285. {
  5286. sReductionFactor = 2;
  5287. }
  5288. else if (pSoldier->uiStatusFlags & SOLDIER_MONSTER)
  5289. {
  5290. switch( pSoldier->ubBodyType )
  5291. {
  5292. case LARVAE_MONSTER:
  5293. case INFANT_MONSTER:
  5294. sReductionFactor = 1;
  5295. break;
  5296. case YAF_MONSTER:
  5297. case YAM_MONSTER:
  5298. sReductionFactor = 4;
  5299. break;
  5300. case ADULTFEMALEMONSTER:
  5301. case AM_MONSTER:
  5302. sReductionFactor = 6;
  5303. break;
  5304. case QUEENMONSTER:
  5305. // increase with range!
  5306. if ( ubAttacker == NOBODY )
  5307. {
  5308. sReductionFactor = 8;
  5309. }
  5310. else
  5311. {
  5312. sReductionFactor = 4 + PythSpacesAway( MercPtrs[ ubAttacker ]->sGridNo, pSoldier->sGridNo ) / 2;
  5313. }
  5314. break;
  5315. }
  5316. }
  5317. if ( ubReason == TAKE_DAMAGE_EXPLOSION )
  5318. {
  5319. sReductionFactor /= 4;
  5320. }
  5321. if ( sReductionFactor > 1 )
  5322. {
  5323. sLifeDeduct = (sLifeDeduct + (sReductionFactor / 2 ) ) / sReductionFactor;
  5324. }
  5325. else if ( ubReason == TAKE_DAMAGE_EXPLOSION )
  5326. {
  5327. // take at most 2/3rds
  5328. sLifeDeduct = (sLifeDeduct * 2) / 3;
  5329. }
  5330. // reduce breath loss to a smaller degree, except for the queen...
  5331. if ( pSoldier->ubBodyType == QUEENMONSTER )
  5332. {
  5333. // in fact, reduce breath loss by MORE!
  5334. sReductionFactor = __min( sReductionFactor, 8 );
  5335. sReductionFactor *= 2;
  5336. }
  5337. else
  5338. {
  5339. sReductionFactor /= 2;
  5340. }
  5341. if ( sReductionFactor > 1 )
  5342. {
  5343. sBreathLoss = (sBreathLoss + (sReductionFactor / 2 ) ) / sReductionFactor;
  5344. }
  5345. }
  5346. if (sLifeDeduct > pSoldier->bLife)
  5347. {
  5348. pSoldier->bLife = 0;
  5349. }
  5350. else
  5351. {
  5352. // Decrease Health
  5353. pSoldier->bLife -= sLifeDeduct;
  5354. }
  5355. // ATE: Put some logic in here to allow enemies to die quicker.....
  5356. // Are we an enemy?
  5357. if ( pSoldier->bSide != gbPlayerNum && !pSoldier->bNeutral && pSoldier->ubProfile == NO_PROFILE )
  5358. {
  5359. // ATE: Give them a chance to fall down...
  5360. if ( pSoldier->bLife > 0 && pSoldier->bLife < ( OKLIFE - 1 ) )
  5361. {
  5362. // Are we taking damage from bleeding?
  5363. if ( ubReason == TAKE_DAMAGE_BLOODLOSS )
  5364. {
  5365. // Fifty-fifty chance to die now!
  5366. if ( Random( 2 ) == 0 || gTacticalStatus.Team[ pSoldier->bTeam ].bMenInSector == 1 )
  5367. {
  5368. // Kill!
  5369. pSoldier->bLife = 0;
  5370. }
  5371. }
  5372. else
  5373. {
  5374. // OK, see how far we are..
  5375. if ( pSoldier->bLife < ( OKLIFE - 3 ) )
  5376. {
  5377. // Kill!
  5378. pSoldier->bLife = 0;
  5379. }
  5380. }
  5381. }
  5382. }
  5383. if ( fShowDamage )
  5384. {
  5385. pSoldier->sDamage += sLifeDeduct;
  5386. }
  5387. // Truncate life
  5388. if ( pSoldier->bLife < 0 )
  5389. {
  5390. pSoldier->bLife = 0;
  5391. }
  5392. // Calculate damage to our items if from an explosion!
  5393. if ( ubReason == TAKE_DAMAGE_EXPLOSION || ubReason == TAKE_DAMAGE_STRUCTURE_EXPLOSION)
  5394. {
  5395. CheckEquipmentForDamage( pSoldier, sLifeDeduct );
  5396. }
  5397. // Calculate bleeding
  5398. if ( ubReason != TAKE_DAMAGE_GAS && !AM_A_ROBOT( pSoldier ) )
  5399. {
  5400. if ( ubReason == TAKE_DAMAGE_HANDTOHAND )
  5401. {
  5402. if ( sLifeDeduct > 0 )
  5403. {
  5404. // HTH does 1 pt bleeding per hit
  5405. pSoldier->bBleeding = pSoldier->bBleeding + 1;
  5406. }
  5407. }
  5408. else
  5409. {
  5410. pSoldier->bBleeding = pSoldier->bLifeMax - ( pSoldier->bLife + bBandage );
  5411. }
  5412. }
  5413. // Deduct breath AND APs!
  5414. sAPCost = (sLifeDeduct / AP_GET_WOUNDED_DIVISOR); // + fallCost;
  5415. // ATE: if the robot, do not deduct
  5416. if ( !AM_A_ROBOT( pSoldier ) )
  5417. {
  5418. DeductPoints( pSoldier, sAPCost, sBreathLoss );
  5419. }
  5420. ubCombinedLoss = (UINT8) sLifeDeduct / 10 + sBreathLoss / 2000;
  5421. // Add shock
  5422. if ( !AM_A_ROBOT( pSoldier ) )
  5423. {
  5424. pSoldier->bShock += ubCombinedLoss;
  5425. }
  5426. // start the stopwatch - the blood is gushing!
  5427. pSoldier->dNextBleed = CalcSoldierNextBleed( pSoldier );
  5428. if ( pSoldier->bInSector && pSoldier->bVisible != -1 )
  5429. {
  5430. // If we are already dead, don't show damage!
  5431. if ( bOldLife != 0 && fShowDamage && sLifeDeduct != 0 && sLifeDeduct < 1000 )
  5432. {
  5433. // Display damage
  5434. INT16 sOffsetX, sOffsetY;
  5435. // Set Damage display counter
  5436. pSoldier->fDisplayDamage = TRUE;
  5437. pSoldier->bDisplayDamageCount = 0;
  5438. if ( pSoldier->ubBodyType == QUEENMONSTER )
  5439. {
  5440. pSoldier->sDamageX = 0;
  5441. pSoldier->sDamageY = 0;
  5442. }
  5443. else
  5444. {
  5445. GetSoldierAnimOffsets( pSoldier, &sOffsetX, &sOffsetY );
  5446. pSoldier->sDamageX = sOffsetX;
  5447. pSoldier->sDamageY = sOffsetY;
  5448. }
  5449. }
  5450. }
  5451. // OK, if here, let's see if we should drop our weapon....
  5452. if ( ubReason != TAKE_DAMAGE_BLOODLOSS && !(AM_A_ROBOT( pSoldier )) )
  5453. {
  5454. INT16 sTestOne, sTestTwo, sChanceToDrop;
  5455. INT8 bVisible = -1;
  5456. sTestOne = EffectiveStrength( pSoldier );
  5457. sTestTwo = ( 2 * ( __max( sLifeDeduct, ( sBreathLoss / 100 ) ) ) );
  5458. if (pSoldier->ubAttackerID != NOBODY && MercPtrs[ pSoldier->ubAttackerID ]->ubBodyType == BLOODCAT )
  5459. {
  5460. // bloodcat boost, let them make people drop items more
  5461. sTestTwo += 20;
  5462. }
  5463. // If damage > effective strength....
  5464. sChanceToDrop = ( __max( 0, ( sTestTwo - sTestOne ) ) );
  5465. // ATE: Increase odds of NOT dropping an UNDROPPABLE OBJECT
  5466. if ( ( pSoldier->inv[ HANDPOS ].fFlags & OBJECT_UNDROPPABLE ) )
  5467. {
  5468. sChanceToDrop -= 30;
  5469. }
  5470. #ifdef JA2TESTVERSION
  5471. //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Chance To Drop Weapon: str: %d Dam: %d Chance: %d", sTestOne, sTestTwo, sChanceToDrop );
  5472. #endif
  5473. if ( Random( 100 ) < (UINT16) sChanceToDrop )
  5474. {
  5475. // OK, drop item in main hand...
  5476. if ( pSoldier->inv[ HANDPOS ].usItem != NOTHING )
  5477. {
  5478. if ( !( pSoldier->inv[ HANDPOS ].fFlags & OBJECT_UNDROPPABLE ) )
  5479. {
  5480. // ATE: if our guy, make visible....
  5481. if ( pSoldier->bTeam == gbPlayerNum )
  5482. {
  5483. bVisible = 1;
  5484. }
  5485. AddItemToPool( pSoldier->sGridNo, &(pSoldier->inv[ HANDPOS ]), bVisible, pSoldier->bLevel, 0, -1 );
  5486. DeleteObj( &(pSoldier->inv[HANDPOS]) );
  5487. }
  5488. }
  5489. }
  5490. }
  5491. // Drop some blood!
  5492. // decide blood amt, if any
  5493. ubBlood = ( sLifeDeduct / BLOODDIVISOR);
  5494. if ( ubBlood > MAXBLOODQUANTITY )
  5495. {
  5496. ubBlood = MAXBLOODQUANTITY;
  5497. }
  5498. if ( !( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE | SOLDIER_ROBOT ) ) )
  5499. {
  5500. if ( ubBlood != 0 )
  5501. {
  5502. if ( pSoldier->bInSector )
  5503. {
  5504. DropBlood( pSoldier, ubBlood, pSoldier->bVisible );
  5505. }
  5506. }
  5507. }
  5508. //Set UI Flag for unconscious, if it's our own guy!
  5509. if ( pSoldier->bTeam == gbPlayerNum )
  5510. {
  5511. if ( pSoldier->bLife < OKLIFE && pSoldier->bLife > 0 && bOldLife >= OKLIFE )
  5512. {
  5513. pSoldier->fUIFirstTimeUNCON = TRUE;
  5514. fInterfacePanelDirty = DIRTYLEVEL2;
  5515. }
  5516. }
  5517. if ( pSoldier->bInSector )
  5518. {
  5519. CheckForBreathCollapse( pSoldier );
  5520. }
  5521. // EXPERIENCE CLASS GAIN (combLoss): Getting wounded in battle
  5522. DirtyMercPanelInterface( pSoldier, DIRTYLEVEL1 );
  5523. if ( ubAttacker != NOBODY )
  5524. {
  5525. // don't give exp for hitting friends!
  5526. if ( (MercPtrs[ ubAttacker ]->bTeam == gbPlayerNum) && (pSoldier->bTeam != gbPlayerNum) )
  5527. {
  5528. if ( ubReason == TAKE_DAMAGE_EXPLOSION )
  5529. {
  5530. // EXPLOSIVES GAIN (combLoss): Causing wounds in battle
  5531. StatChange( MercPtrs[ ubAttacker ], EXPLODEAMT, (UINT16)( 10 * ubCombinedLoss ), FROM_FAILURE );
  5532. }
  5533. /*
  5534. else if ( ubReason == TAKE_DAMAGE_GUNFIRE )
  5535. {
  5536. // MARKSMANSHIP GAIN (combLoss): Causing wounds in battle
  5537. StatChange( MercPtrs[ ubAttacker ], MARKAMT, (UINT16)( 5 * ubCombinedLoss ), FALSE );
  5538. }
  5539. */
  5540. }
  5541. }
  5542. if (PTR_OURTEAM)
  5543. {
  5544. // EXPERIENCE GAIN: Took some damage
  5545. StatChange( pSoldier, EXPERAMT, ( UINT16 )( 5 * ubCombinedLoss ), FROM_FAILURE );
  5546. // Check for quote
  5547. if ( !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_BEING_PUMMELED ) )
  5548. {
  5549. // Check attacker!
  5550. if ( ubAttacker != NOBODY && ubAttacker != pSoldier->ubID )
  5551. {
  5552. pSoldier->bNumHitsThisTurn++;
  5553. if ( (pSoldier->bNumHitsThisTurn >= 3) && ( pSoldier->bLife - pSoldier->bOldLife > 20 ) )
  5554. {
  5555. if ( Random(100) < (UINT16)((40 * ( pSoldier->bNumHitsThisTurn - 2))))
  5556. {
  5557. DelayedTacticalCharacterDialogue( pSoldier, QUOTE_TAKEN_A_BREATING );
  5558. pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_BEING_PUMMELED;
  5559. pSoldier->bNumHitsThisTurn = 0;
  5560. }
  5561. }
  5562. }
  5563. }
  5564. }
  5565. if ((ubAttacker != NOBODY) && (Menptr[ubAttacker].bTeam == OUR_TEAM) && (pSoldier->ubProfile != NO_PROFILE) && (pSoldier->ubProfile >= FIRST_RPC))
  5566. {
  5567. gMercProfiles[pSoldier->ubProfile].ubMiscFlags |= PROFILE_MISC_FLAG_WOUNDEDBYPLAYER;
  5568. if (pSoldier->ubProfile == 114)
  5569. {
  5570. SetFactTrue( FACT_PACOS_KILLED );
  5571. }
  5572. }
  5573. HandleTakeDamageDeath( pSoldier, bOldLife, ubReason );
  5574. // Check if we are < unconscious, and shutup if so! also wipe sight
  5575. if ( pSoldier->bLife < CONSCIOUSNESS )
  5576. {
  5577. ShutupaYoFace( pSoldier->iFaceIndex );
  5578. }
  5579. if ( pSoldier->bLife < OKLIFE )
  5580. {
  5581. DecayIndividualOpplist( pSoldier );
  5582. }
  5583. return( ubCombinedLoss );
  5584. }
  5585. extern BOOLEAN IsMercSayingDialogue( UINT8 ubProfileID );
  5586. BOOLEAN InternalDoMercBattleSound( SOLDIERTYPE *pSoldier, UINT8 ubBattleSoundID, INT8 bSpecialCode )
  5587. {
  5588. SGPFILENAME zFilename;
  5589. SOUNDPARMS spParms;
  5590. UINT8 ubSoundID;
  5591. UINT32 uiSoundID;
  5592. UINT32 iFaceIndex;
  5593. BOOLEAN fDoSub = FALSE;
  5594. INT32 uiSubSoundID = 0;
  5595. BOOLEAN fSpeechSound = FALSE;
  5596. // DOUBLECHECK RANGE
  5597. CHECKF ( ubBattleSoundID < NUM_MERC_BATTLE_SOUNDS );
  5598. if ( ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
  5599. {
  5600. // Pick a passenger from vehicle....
  5601. pSoldier = PickRandomPassengerFromVehicle( pSoldier );
  5602. if ( pSoldier == NULL )
  5603. {
  5604. return( FALSE );
  5605. }
  5606. }
  5607. // If a death sound, and we have already done ours...
  5608. if ( ubBattleSoundID == BATTLE_SOUND_DIE1 )
  5609. {
  5610. if ( pSoldier->fDieSoundUsed )
  5611. {
  5612. return( TRUE );
  5613. }
  5614. }
  5615. // Are we mute?
  5616. if ( pSoldier->uiStatusFlags & SOLDIER_MUTE )
  5617. {
  5618. return( FALSE );
  5619. }
  5620. // uiTimeSameBattleSndDone
  5621. // If we are a creature, etc, pick a better sound...
  5622. if ( ubBattleSoundID == BATTLE_SOUND_HIT1 || ubBattleSoundID == BATTLE_SOUND_HIT2 )
  5623. {
  5624. switch ( pSoldier->ubBodyType )
  5625. {
  5626. case COW:
  5627. fDoSub = TRUE;
  5628. uiSubSoundID = COW_HIT_SND;
  5629. break;
  5630. case YAF_MONSTER:
  5631. case YAM_MONSTER:
  5632. case ADULTFEMALEMONSTER:
  5633. case AM_MONSTER:
  5634. fDoSub = TRUE;
  5635. if ( Random( 2 ) == 0 )
  5636. {
  5637. uiSubSoundID = ACR_DIE_PART1;
  5638. }
  5639. else
  5640. {
  5641. uiSubSoundID = ACR_LUNGE;
  5642. }
  5643. break;
  5644. case INFANT_MONSTER:
  5645. fDoSub = TRUE;
  5646. uiSubSoundID = BCR_SHRIEK;
  5647. break;
  5648. case QUEENMONSTER:
  5649. fDoSub = TRUE;
  5650. uiSubSoundID = LQ_SHRIEK;
  5651. break;
  5652. case LARVAE_MONSTER:
  5653. fDoSub = TRUE;
  5654. uiSubSoundID = BCR_SHRIEK;
  5655. break;
  5656. case BLOODCAT:
  5657. fDoSub = TRUE;
  5658. uiSubSoundID = BLOODCAT_HIT_1;
  5659. break;
  5660. case ROBOTNOWEAPON:
  5661. fDoSub = TRUE;
  5662. uiSubSoundID = (UINT32)( S_METAL_IMPACT1 + Random( 2 ) );
  5663. break;
  5664. }
  5665. }
  5666. if ( ubBattleSoundID == BATTLE_SOUND_DIE1 )
  5667. {
  5668. switch ( pSoldier->ubBodyType )
  5669. {
  5670. case COW:
  5671. fDoSub = TRUE;
  5672. uiSubSoundID = COW_DIE_SND;
  5673. break;
  5674. case YAF_MONSTER:
  5675. case YAM_MONSTER:
  5676. case ADULTFEMALEMONSTER:
  5677. case AM_MONSTER:
  5678. fDoSub = TRUE;
  5679. uiSubSoundID = CREATURE_FALL_PART_2;
  5680. break;
  5681. case INFANT_MONSTER:
  5682. fDoSub = TRUE;
  5683. uiSubSoundID = BCR_DYING;
  5684. break;
  5685. case LARVAE_MONSTER:
  5686. fDoSub = TRUE;
  5687. uiSubSoundID = LCR_RUPTURE;
  5688. break;
  5689. case QUEENMONSTER:
  5690. fDoSub = TRUE;
  5691. uiSubSoundID = LQ_DYING;
  5692. break;
  5693. case BLOODCAT:
  5694. fDoSub = TRUE;
  5695. uiSubSoundID = BLOODCAT_DIE_1;
  5696. break;
  5697. case ROBOTNOWEAPON:
  5698. fDoSub = TRUE;
  5699. uiSubSoundID = (UINT32)( EXPLOSION_1 );
  5700. PlayJA2Sample( ROBOT_DEATH, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
  5701. break;
  5702. }
  5703. }
  5704. // OK. any other sound, not hits, robot makes a beep
  5705. if ( pSoldier->ubBodyType == ROBOTNOWEAPON && !fDoSub )
  5706. {
  5707. fDoSub = TRUE;
  5708. if ( ubBattleSoundID == BATTLE_SOUND_ATTN1 )
  5709. {
  5710. uiSubSoundID = ROBOT_GREETING;
  5711. }
  5712. else
  5713. {
  5714. uiSubSoundID = ROBOT_BEEP;
  5715. }
  5716. }
  5717. if ( fDoSub )
  5718. {
  5719. if( guiCurrentScreen != GAME_SCREEN )
  5720. {
  5721. PlayJA2Sample( uiSubSoundID, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
  5722. }
  5723. else
  5724. {
  5725. PlayJA2Sample( uiSubSoundID, RATE_11025, SoundVolume( (UINT8)CalculateSpeechVolume( HIGHVOLUME ), pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
  5726. }
  5727. return( TRUE );
  5728. }
  5729. // Check if this is the same one we just played...
  5730. if ( pSoldier->bOldBattleSnd == ubBattleSoundID && gBattleSndsData[ ubBattleSoundID ].fDontAllowTwoInRow )
  5731. {
  5732. // Are we below the min delay?
  5733. if ( ( GetJA2Clock( ) - pSoldier->uiTimeSameBattleSndDone ) < MIN_SUBSEQUENT_SNDS_DELAY )
  5734. {
  5735. return( TRUE );
  5736. }
  5737. }
  5738. // If a battle snd is STILL playing....
  5739. if ( SoundIsPlaying( pSoldier->uiBattleSoundID ) )
  5740. {
  5741. // We can do a few things here....
  5742. // Is this a crutial one...?
  5743. if ( gBattleSndsData[ ubBattleSoundID ].fStopDialogue == 1 )
  5744. {
  5745. // Stop playing origonal
  5746. SoundStop( pSoldier->uiBattleSoundID );
  5747. }
  5748. else
  5749. {
  5750. // Skip this one...
  5751. return( TRUE );
  5752. }
  5753. }
  5754. // If we are talking now....
  5755. if ( IsMercSayingDialogue( pSoldier->ubProfile ) )
  5756. {
  5757. // We can do a couple of things now...
  5758. if ( gBattleSndsData[ ubBattleSoundID ].fStopDialogue == 1 )
  5759. {
  5760. // Stop dialigue...
  5761. DialogueAdvanceSpeech( );
  5762. }
  5763. else if ( gBattleSndsData[ ubBattleSoundID ].fStopDialogue == 2 )
  5764. {
  5765. // Skip battle snd...
  5766. return( TRUE );
  5767. }
  5768. }
  5769. // Save this one we're doing...
  5770. pSoldier->bOldBattleSnd = ubBattleSoundID;
  5771. pSoldier->uiTimeSameBattleSndDone = GetJA2Clock( );
  5772. // Adjust based on morale...
  5773. if ( ubBattleSoundID == BATTLE_SOUND_OK1 && pSoldier->bMorale < LOW_MORALE_BATTLE_SND_THREASHOLD )
  5774. {
  5775. ubBattleSoundID = BATTLE_SOUND_LOWMARALE_OK1;
  5776. }
  5777. if ( ubBattleSoundID == BATTLE_SOUND_ATTN1 && pSoldier->bMorale < LOW_MORALE_BATTLE_SND_THREASHOLD )
  5778. {
  5779. ubBattleSoundID = BATTLE_SOUND_LOWMARALE_ATTN1;
  5780. }
  5781. ubSoundID = ubBattleSoundID;
  5782. //if the sound to be played is a confirmation, check to see if we are to play it
  5783. if( ubSoundID == BATTLE_SOUND_OK1 )
  5784. {
  5785. if( gGameSettings.fOptions[ TOPTION_MUTE_CONFIRMATIONS ] )
  5786. return( TRUE );
  5787. //else a speech sound is to be played
  5788. else
  5789. fSpeechSound = TRUE;
  5790. }
  5791. // Randomize between sounds, if appropriate
  5792. if ( gBattleSndsData[ ubSoundID ].ubRandomVal != 0 )
  5793. {
  5794. ubSoundID = ubSoundID + (UINT8)Random( gBattleSndsData[ ubSoundID ].ubRandomVal );
  5795. }
  5796. // OK, build file and play!
  5797. if ( pSoldier->ubProfile != NO_PROFILE )
  5798. {
  5799. sprintf( zFilename, "BATTLESNDS\\%03d_%s.wav", pSoldier->ubProfile, gBattleSndsData[ ubSoundID ].zName );
  5800. if ( !FileExists( zFilename ) )
  5801. {
  5802. // OK, temp build file...
  5803. if ( pSoldier->ubBodyType == REGFEMALE )
  5804. {
  5805. sprintf( zFilename, "BATTLESNDS\\f_%s.wav", gBattleSndsData[ ubSoundID ].zName );
  5806. }
  5807. else
  5808. {
  5809. sprintf( zFilename, "BATTLESNDS\\m_%s.wav", gBattleSndsData[ ubSoundID ].zName );
  5810. }
  5811. }
  5812. }
  5813. else
  5814. {
  5815. // Check if we can play this!
  5816. if ( !gBattleSndsData[ ubSoundID ].fBadGuy )
  5817. {
  5818. return( FALSE );
  5819. }
  5820. if ( pSoldier->ubBodyType == HATKIDCIV || pSoldier->ubBodyType == KIDCIV )
  5821. {
  5822. if ( ubSoundID == BATTLE_SOUND_DIE1 )
  5823. {
  5824. sprintf( zFilename, "BATTLESNDS\\kid%d_dying.wav", pSoldier->ubBattleSoundID );
  5825. }
  5826. else
  5827. {
  5828. sprintf( zFilename, "BATTLESNDS\\kid%d_%s.wav", pSoldier->ubBattleSoundID, gBattleSndsData[ ubSoundID ].zName );
  5829. }
  5830. }
  5831. else
  5832. {
  5833. if ( ubSoundID == BATTLE_SOUND_DIE1 )
  5834. {
  5835. sprintf( zFilename, "BATTLESNDS\\bad%d_die.wav", pSoldier->ubBattleSoundID );
  5836. }
  5837. else
  5838. {
  5839. sprintf( zFilename, "BATTLESNDS\\bad%d_%s.wav", pSoldier->ubBattleSoundID, gBattleSndsData[ ubSoundID ].zName );
  5840. }
  5841. }
  5842. }
  5843. // Play sound!
  5844. memset(&spParms, 0xff, sizeof(SOUNDPARMS));
  5845. spParms.uiSpeed = RATE_11025;
  5846. //spParms.uiVolume = CalculateSpeechVolume( pSoldier->bVocalVolume );
  5847. spParms.uiVolume = (INT8)CalculateSpeechVolume( HIGHVOLUME );
  5848. // ATE: Reduce volume for OK sounds...
  5849. // ( Only for all-moves or multi-selection cases... )
  5850. if ( bSpecialCode == BATTLE_SND_LOWER_VOLUME )
  5851. {
  5852. spParms.uiVolume = (INT8)CalculateSpeechVolume( MIDVOLUME );
  5853. }
  5854. // If we are an enemy.....reduce due to volume
  5855. if ( pSoldier->bTeam != gbPlayerNum )
  5856. {
  5857. spParms.uiVolume = SoundVolume( (UINT8)spParms.uiVolume, pSoldier->sGridNo );
  5858. }
  5859. spParms.uiLoop = 1;
  5860. spParms.uiPan = SoundDir( pSoldier->sGridNo );
  5861. spParms.uiPriority=GROUP_PLAYER;
  5862. if ( ( uiSoundID = SoundPlay( zFilename, &spParms ) ) == SOUND_ERROR )
  5863. {
  5864. return( FALSE );
  5865. }
  5866. else
  5867. {
  5868. pSoldier->uiBattleSoundID = uiSoundID;
  5869. if ( pSoldier->ubProfile != NO_PROFILE )
  5870. {
  5871. // Get soldier's face ID
  5872. iFaceIndex = pSoldier->iFaceIndex;
  5873. // Check face index
  5874. if ( iFaceIndex != -1 )
  5875. {
  5876. ExternSetFaceTalking( iFaceIndex, uiSoundID );
  5877. }
  5878. }
  5879. return( TRUE );
  5880. }
  5881. }
  5882. BOOLEAN DoMercBattleSound( SOLDIERTYPE *pSoldier, UINT8 ubBattleSoundID )
  5883. {
  5884. // We WANT to play some RIGHT AWAY.....
  5885. if ( gBattleSndsData[ ubBattleSoundID ].fStopDialogue == 1 || ( pSoldier->ubProfile == NO_PROFILE ) || InOverheadMap( ) )
  5886. {
  5887. return( InternalDoMercBattleSound( pSoldier, ubBattleSoundID, 0 ) );
  5888. }
  5889. // So here, only if we were currently saying dialogue.....
  5890. if ( !IsMercSayingDialogue( pSoldier->ubProfile ) )
  5891. {
  5892. return( InternalDoMercBattleSound( pSoldier, ubBattleSoundID, 0 ) );
  5893. }
  5894. // OK, queue it up otherwise!
  5895. TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_DO_BATTLE_SND, ubBattleSoundID,0 );
  5896. return( TRUE );
  5897. }
  5898. BOOLEAN PreloadSoldierBattleSounds( SOLDIERTYPE *pSoldier, BOOLEAN fRemove )
  5899. {
  5900. UINT32 cnt;
  5901. CHECKF( pSoldier->bActive != FALSE );
  5902. for ( cnt = 0; cnt < NUM_MERC_BATTLE_SOUNDS; cnt++ )
  5903. {
  5904. // OK, build file and play!
  5905. if ( pSoldier->ubProfile != NO_PROFILE )
  5906. {
  5907. if ( gBattleSndsData[ cnt ].fPreload )
  5908. {
  5909. if ( fRemove )
  5910. {
  5911. SoundUnlockSample( gBattleSndsData[ cnt ].zName );
  5912. }
  5913. else
  5914. {
  5915. SoundLockSample( gBattleSndsData[ cnt ].zName );
  5916. }
  5917. }
  5918. }
  5919. else
  5920. {
  5921. if ( gBattleSndsData[ cnt ].fPreload && gBattleSndsData[ cnt ].fBadGuy )
  5922. {
  5923. if ( fRemove )
  5924. {
  5925. SoundUnlockSample( gBattleSndsData[ cnt ].zName );
  5926. }
  5927. else
  5928. {
  5929. SoundLockSample( gBattleSndsData[ cnt ].zName );
  5930. }
  5931. }
  5932. }
  5933. }
  5934. return( TRUE );
  5935. }
  5936. BOOLEAN CheckSoldierHitRoof( SOLDIERTYPE *pSoldier )
  5937. {
  5938. // Check if we are near a lower level
  5939. INT8 bNewDirection;
  5940. BOOLEAN fReturnVal = FALSE;
  5941. INT16 sNewGridNo;
  5942. // Default to true
  5943. BOOLEAN fDoForwards = TRUE;
  5944. if ( pSoldier->bLife >= OKLIFE )
  5945. {
  5946. return( FALSE );
  5947. }
  5948. if ( FindLowerLevel( pSoldier, pSoldier->sGridNo, pSoldier->bDirection, &bNewDirection ) && ( pSoldier->bLevel > 0 ) )
  5949. {
  5950. // ONly if standing!
  5951. if ( gAnimControl[ pSoldier->usAnimState ].ubHeight == ANIM_STAND )
  5952. {
  5953. // We are near a lower level.
  5954. // Use opposite direction
  5955. bNewDirection = gOppositeDirection[ bNewDirection ];
  5956. // Alrighty, let's not blindly change here, look at whether the dest gridno is good!
  5957. sNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ bNewDirection ] ) );
  5958. if ( !NewOKDestination( pSoldier, sNewGridNo, TRUE, 0 ) )
  5959. {
  5960. return( FALSE );
  5961. }
  5962. sNewGridNo = NewGridNo( (UINT16)sNewGridNo, DirectionInc( gOppositeDirection[ bNewDirection ] ) );
  5963. if ( !NewOKDestination( pSoldier, sNewGridNo, TRUE, 0 ) )
  5964. {
  5965. return( FALSE );
  5966. }
  5967. // Are wee near enough to fall forwards....
  5968. if ( pSoldier->bDirection == gOneCDirection[ bNewDirection ] ||
  5969. pSoldier->bDirection == gTwoCDirection[ bNewDirection ] ||
  5970. pSoldier->bDirection == bNewDirection ||
  5971. pSoldier->bDirection == gOneCCDirection[ bNewDirection ] ||
  5972. pSoldier->bDirection == gTwoCCDirection[ bNewDirection ] )
  5973. {
  5974. // Do backwards...
  5975. fDoForwards = FALSE;
  5976. }
  5977. // If we are facing the opposite direction, fall backwards
  5978. // ATE: Make this more usefull...
  5979. if ( fDoForwards )
  5980. {
  5981. pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (INT16)( -1 * DirectionInc(bNewDirection ) ) );
  5982. pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sTempNewGridNo, (INT16)( -1 * DirectionInc( bNewDirection ) ) );
  5983. EVENT_SetSoldierDesiredDirection( pSoldier, gOppositeDirection[ bNewDirection ] );
  5984. pSoldier->fTurningUntilDone = TRUE;
  5985. pSoldier->usPendingAnimation = FALLFORWARD_ROOF;
  5986. //EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_ROOF, 0 , FALSE );
  5987. // Deduct hitpoints/breath for falling!
  5988. SoldierTakeDamage( pSoldier, ANIM_CROUCH, 100, 5000, TAKE_DAMAGE_FALLROOF, NOBODY, NOWHERE, 0, TRUE );
  5989. fReturnVal = TRUE;
  5990. }
  5991. else
  5992. {
  5993. pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (INT16)( -1 * DirectionInc( bNewDirection ) ) );
  5994. pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sTempNewGridNo, (INT16)( -1 * DirectionInc( bNewDirection ) ) );
  5995. EVENT_SetSoldierDesiredDirection( pSoldier, bNewDirection );
  5996. pSoldier->fTurningUntilDone = TRUE;
  5997. pSoldier->usPendingAnimation = FALLOFF;
  5998. // Deduct hitpoints/breath for falling!
  5999. SoldierTakeDamage( pSoldier, ANIM_CROUCH, 100, 5000, TAKE_DAMAGE_FALLROOF, NOBODY, NOWHERE, 0, TRUE );
  6000. fReturnVal = TRUE;
  6001. }
  6002. }
  6003. }
  6004. return( fReturnVal );
  6005. }
  6006. void BeginSoldierClimbDownRoof( SOLDIERTYPE *pSoldier )
  6007. {
  6008. INT8 bNewDirection;
  6009. if ( FindLowerLevel( pSoldier, pSoldier->sGridNo, pSoldier->bDirection, &bNewDirection ) && ( pSoldier->bLevel > 0 ) )
  6010. {
  6011. if ( EnoughPoints( pSoldier, GetAPsToClimbRoof( pSoldier, TRUE ), 0, TRUE ) )
  6012. {
  6013. if (pSoldier->bTeam == gbPlayerNum)
  6014. {
  6015. // OK, SET INTERFACE FIRST
  6016. SetUIBusy( pSoldier->ubID );
  6017. }
  6018. pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bNewDirection ) );
  6019. bNewDirection = gTwoCDirection[ bNewDirection ];
  6020. pSoldier->ubPendingDirection = bNewDirection;
  6021. EVENT_InitNewSoldierAnim( pSoldier, CLIMBDOWNROOF, 0 , FALSE );
  6022. InternalReceivingSoldierCancelServices( pSoldier, FALSE );
  6023. InternalGivingSoldierCancelServices( pSoldier, FALSE );
  6024. }
  6025. }
  6026. }
  6027. void MoveMerc( SOLDIERTYPE *pSoldier, FLOAT dMovementChange, FLOAT dAngle, BOOLEAN fCheckRange )
  6028. {
  6029. INT16 dDegAngle;
  6030. FLOAT dDeltaPos;
  6031. FLOAT dXPos , dYPos;
  6032. BOOLEAN fStop = FALSE;
  6033. dDegAngle = (INT16)( dAngle * 180 / PI );
  6034. //sprintf( gDebugStr, "Move Angle: %d", (int)dDegAngle );
  6035. // Find delta Movement for X pos
  6036. dDeltaPos = (FLOAT) (dMovementChange * sin( dAngle ));
  6037. // Find new position
  6038. dXPos = pSoldier->dXPos + dDeltaPos;
  6039. if ( fCheckRange )
  6040. {
  6041. fStop = FALSE;
  6042. switch( pSoldier->bMovementDirection )
  6043. {
  6044. case NORTHEAST:
  6045. case EAST:
  6046. case SOUTHEAST:
  6047. if ( dXPos >= pSoldier->sDestXPos )
  6048. {
  6049. fStop = TRUE;
  6050. }
  6051. break;
  6052. case NORTHWEST:
  6053. case WEST:
  6054. case SOUTHWEST:
  6055. if ( dXPos <= pSoldier->sDestXPos )
  6056. {
  6057. fStop = TRUE;
  6058. }
  6059. break;
  6060. case NORTH:
  6061. case SOUTH:
  6062. fStop = TRUE;
  6063. break;
  6064. }
  6065. if ( fStop )
  6066. {
  6067. //dXPos = pSoldier->sDestXPos;
  6068. pSoldier->fPastXDest = TRUE;
  6069. if ( pSoldier->sGridNo == pSoldier->sFinalDestination )
  6070. {
  6071. dXPos = pSoldier->sDestXPos;
  6072. }
  6073. }
  6074. }
  6075. // Find delta Movement for Y pos
  6076. dDeltaPos = (FLOAT) (dMovementChange * cos( dAngle ));
  6077. // Find new pos
  6078. dYPos = pSoldier->dYPos + dDeltaPos;
  6079. if ( fCheckRange )
  6080. {
  6081. fStop = FALSE;
  6082. switch( pSoldier->bMovementDirection )
  6083. {
  6084. case NORTH:
  6085. case NORTHEAST:
  6086. case NORTHWEST:
  6087. if ( dYPos <= pSoldier->sDestYPos )
  6088. {
  6089. fStop = TRUE;
  6090. }
  6091. break;
  6092. case SOUTH:
  6093. case SOUTHWEST:
  6094. case SOUTHEAST:
  6095. if ( dYPos >= pSoldier->sDestYPos )
  6096. {
  6097. fStop = TRUE;
  6098. }
  6099. break;
  6100. case EAST:
  6101. case WEST:
  6102. fStop = TRUE;
  6103. break;
  6104. }
  6105. if ( fStop )
  6106. {
  6107. //dYPos = pSoldier->sDestYPos;
  6108. pSoldier->fPastYDest = TRUE;
  6109. if ( pSoldier->sGridNo == pSoldier->sFinalDestination )
  6110. {
  6111. dYPos = pSoldier->sDestYPos;
  6112. }
  6113. }
  6114. }
  6115. // OK, set new position
  6116. EVENT_InternalSetSoldierPosition( pSoldier, dXPos, dYPos, FALSE, FALSE, FALSE );
  6117. // DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("X: %f Y: %f", dXPos, dYPos ) );
  6118. }
  6119. INT16 GetDirectionFromGridNo( INT16 sGridNo, SOLDIERTYPE *pSoldier )
  6120. {
  6121. INT16 sXPos, sYPos;
  6122. ConvertGridNoToXY( sGridNo, &sXPos, &sYPos );
  6123. return( GetDirectionFromXY( sXPos, sYPos, pSoldier ) );
  6124. }
  6125. INT16 GetDirectionToGridNoFromGridNo( INT16 sGridNoDest, INT16 sGridNoSrc )
  6126. {
  6127. INT16 sXPos2, sYPos2;
  6128. INT16 sXPos, sYPos;
  6129. ConvertGridNoToXY( sGridNoSrc, &sXPos, &sYPos );
  6130. ConvertGridNoToXY( sGridNoDest, &sXPos2, &sYPos2 );
  6131. return( atan8( sXPos2, sYPos2, sXPos, sYPos ) );
  6132. }
  6133. INT16 GetDirectionFromXY( INT16 sXPos, INT16 sYPos, SOLDIERTYPE *pSoldier )
  6134. {
  6135. INT16 sXPos2, sYPos2;
  6136. ConvertGridNoToXY( pSoldier->sGridNo, &sXPos2, &sYPos2 );
  6137. return( atan8( sXPos2, sYPos2, sXPos, sYPos ) );
  6138. }
  6139. #if 0
  6140. UINT8 atan8( INT16 x1, INT16 y1, INT16 x2, INT16 y2 )
  6141. {
  6142. static int trig[8] = { 2, 3, 4, 5, 6, 7, 8, 1 };
  6143. // returned values are N=1, NE=2, E=3, SE=4, S=5, SW=6, W=7, NW=8
  6144. double dx=(x2-x1);
  6145. double dy=(y2-y1);
  6146. double a;
  6147. int i,k;
  6148. if (dx==0)
  6149. dx=0.00390625; // 1/256th
  6150. #define PISLICES (8)
  6151. a=(atan2(dy,dx) + PI/PISLICES)/(PI/(PISLICES/2));
  6152. i=(int)a;
  6153. if (a>0)
  6154. k=i; else
  6155. if (a<0)
  6156. k=i+(PISLICES-1); else
  6157. k=0;
  6158. return(trig[k]);
  6159. }
  6160. #endif
  6161. //#if 0
  6162. UINT8 atan8( INT16 sXPos, INT16 sYPos, INT16 sXPos2, INT16 sYPos2 )
  6163. {
  6164. DOUBLE test_x = sXPos2 - sXPos;
  6165. DOUBLE test_y = sYPos2 - sYPos;
  6166. UINT8 mFacing = WEST;
  6167. INT16 dDegAngle;
  6168. DOUBLE angle;
  6169. if ( test_x == 0 )
  6170. {
  6171. test_x = 0.04;
  6172. }
  6173. angle = atan2( test_x, test_y );
  6174. dDegAngle = (INT16)( angle * 180 / PI );
  6175. //sprintf( gDebugStr, "Move Angle: %d", (int)dDegAngle );
  6176. do
  6177. {
  6178. if ( angle >=-PI*.375 && angle <= -PI*.125 )
  6179. {
  6180. mFacing = SOUTHWEST;
  6181. break;
  6182. }
  6183. if ( angle <= PI*.375 && angle >= PI*.125 )
  6184. {
  6185. mFacing = SOUTHEAST;
  6186. break;
  6187. }
  6188. if ( angle >=PI*.623 && angle <= PI*.875 )
  6189. {
  6190. mFacing = NORTHEAST;
  6191. break;
  6192. }
  6193. if ( angle <=-PI*.623 && angle >= -PI*.875 )
  6194. {
  6195. mFacing = NORTHWEST;
  6196. break;
  6197. }
  6198. if ( angle >-PI*0.125 && angle < PI*0.125 )
  6199. {
  6200. mFacing = SOUTH;
  6201. }
  6202. if ( angle > PI*0.375 && angle < PI*0.623 )
  6203. {
  6204. mFacing = EAST;
  6205. }
  6206. if ( ( angle > PI*0.875 && angle <= PI ) || ( angle > -PI && angle < -PI*0.875 ) )
  6207. {
  6208. mFacing = NORTH;
  6209. }
  6210. if ( angle > -PI*0.623 && angle < -PI*0.375 )
  6211. {
  6212. mFacing = WEST;
  6213. }
  6214. } while( FALSE );
  6215. return( mFacing );
  6216. }
  6217. UINT8 atan8FromAngle( DOUBLE angle )
  6218. {
  6219. UINT8 mFacing = WEST;
  6220. if ( angle > PI )
  6221. {
  6222. angle = ( angle - PI ) - PI;
  6223. }
  6224. if ( angle < -PI )
  6225. {
  6226. angle = ( PI - ( fabs( angle ) - PI ) );
  6227. }
  6228. do
  6229. {
  6230. if ( angle >=-PI*.375 && angle <= -PI*.125 )
  6231. {
  6232. mFacing = SOUTHWEST;
  6233. break;
  6234. }
  6235. if ( angle <= PI*.375 && angle >= PI*.125 )
  6236. {
  6237. mFacing = SOUTHEAST;
  6238. break;
  6239. }
  6240. if ( angle >=PI*.623 && angle <= PI*.875 )
  6241. {
  6242. mFacing = NORTHEAST;
  6243. break;
  6244. }
  6245. if ( angle <=-PI*.623 && angle >= -PI*.875 )
  6246. {
  6247. mFacing = NORTHWEST;
  6248. break;
  6249. }
  6250. if ( angle >-PI*0.125 && angle < PI*0.125 )
  6251. {
  6252. mFacing = SOUTH;
  6253. }
  6254. if ( angle > PI*0.375 && angle < PI*0.623 )
  6255. {
  6256. mFacing = EAST;
  6257. }
  6258. if ( ( angle > PI*0.875 && angle <= PI ) || ( angle > -PI && angle < -PI*0.875 ) )
  6259. {
  6260. mFacing = NORTH;
  6261. }
  6262. if ( angle > -PI*0.623 && angle < -PI*0.375 )
  6263. {
  6264. mFacing = WEST;
  6265. }
  6266. } while( FALSE );
  6267. return( mFacing );
  6268. }
  6269. void CheckForFullStructures( SOLDIERTYPE *pSoldier )
  6270. {
  6271. // This function checks to see if we are near a specific structure type which requires us to blit a
  6272. // small obscuring peice
  6273. INT16 sGridNo;
  6274. UINT16 usFullTileIndex;
  6275. INT32 cnt;
  6276. // Check in all 'Above' directions
  6277. for ( cnt = 0; cnt < MAX_FULLTILE_DIRECTIONS; cnt++ )
  6278. {
  6279. sGridNo = pSoldier->sGridNo + gsFullTileDirections[ cnt ];
  6280. if ( CheckForFullStruct( sGridNo, &usFullTileIndex ) )
  6281. {
  6282. // Add one for the item's obsuring part
  6283. pSoldier->usFrontArcFullTileList[ cnt ] = usFullTileIndex + 1;
  6284. pSoldier->usFrontArcFullTileGridNos[ cnt ] = sGridNo;
  6285. AddTopmostToHead( sGridNo, pSoldier->usFrontArcFullTileList[ cnt ] );
  6286. }
  6287. else
  6288. {
  6289. if ( pSoldier->usFrontArcFullTileList[ cnt ] != 0 )
  6290. {
  6291. RemoveTopmost( pSoldier->usFrontArcFullTileGridNos[ cnt ], pSoldier->usFrontArcFullTileList[ cnt ] );
  6292. }
  6293. pSoldier->usFrontArcFullTileList[ cnt ] = 0;
  6294. pSoldier->usFrontArcFullTileGridNos[ cnt ] = 0;
  6295. }
  6296. }
  6297. }
  6298. BOOLEAN CheckForFullStruct( INT16 sGridNo, UINT16 *pusIndex )
  6299. {
  6300. LEVELNODE *pStruct = NULL;
  6301. LEVELNODE *pOldStruct = NULL;
  6302. UINT32 fTileFlags;
  6303. pStruct = gpWorldLevelData[ sGridNo ].pStructHead;
  6304. // Look through all structs and Search for type
  6305. while( pStruct != NULL )
  6306. {
  6307. if ( pStruct->usIndex != NO_TILE && pStruct->usIndex < NUMBEROFTILES )
  6308. {
  6309. GetTileFlags( pStruct->usIndex, &fTileFlags );
  6310. // Advance to next
  6311. pOldStruct = pStruct;
  6312. pStruct = pStruct->pNext;
  6313. //if( (pOldStruct->pStructureData!=NULL) && ( pOldStruct->pStructureData->fFlags&STRUCTURE_TREE ) )
  6314. if ( fTileFlags & FULL3D_TILE )
  6315. {
  6316. // CHECK IF THIS TREE IS FAIRLY ALONE!
  6317. if ( FullStructAlone( sGridNo, 2 ) )
  6318. {
  6319. // Return true and return index
  6320. *pusIndex = pOldStruct->usIndex;
  6321. return( TRUE );
  6322. }
  6323. else
  6324. {
  6325. return( FALSE );
  6326. }
  6327. }
  6328. }
  6329. else
  6330. {
  6331. // Advance to next
  6332. pOldStruct = pStruct;
  6333. pStruct = pStruct->pNext;
  6334. }
  6335. }
  6336. // Could not find it, return FALSE
  6337. return( FALSE );
  6338. }
  6339. BOOLEAN FullStructAlone( INT16 sGridNo, UINT8 ubRadius )
  6340. {
  6341. INT16 sTop, sBottom;
  6342. INT16 sLeft, sRight;
  6343. INT16 cnt1, cnt2;
  6344. INT16 iNewIndex;
  6345. INT32 leftmost;
  6346. // Determine start end end indicies and num rows
  6347. sTop = ubRadius;
  6348. sBottom = -ubRadius;
  6349. sLeft = - ubRadius;
  6350. sRight = ubRadius;
  6351. for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
  6352. {
  6353. leftmost = ( ( sGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS;
  6354. for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ )
  6355. {
  6356. iNewIndex = sGridNo + ( WORLD_COLS * cnt1 ) + cnt2;
  6357. if ( iNewIndex >=0 && iNewIndex < WORLD_MAX &&
  6358. iNewIndex >= leftmost && iNewIndex < ( leftmost + WORLD_COLS ) )
  6359. {
  6360. if ( iNewIndex != sGridNo )
  6361. {
  6362. if ( FindStructure( iNewIndex, STRUCTURE_TREE ) != NULL )
  6363. {
  6364. return( FALSE );
  6365. }
  6366. }
  6367. }
  6368. }
  6369. }
  6370. return( TRUE );
  6371. }
  6372. void AdjustForFastTurnAnimation( SOLDIERTYPE *pSoldier )
  6373. {
  6374. // CHECK FOR FASTTURN ANIMATIONS
  6375. // ATE: Mod: Only fastturn for OUR guys!
  6376. if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FASTTURN && pSoldier->bTeam == gbPlayerNum && !( pSoldier->uiStatusFlags & SOLDIER_TURNINGFROMHIT ) )
  6377. {
  6378. if ( pSoldier->bDirection != pSoldier->bDesiredDirection )
  6379. {
  6380. pSoldier->sAniDelay = FAST_TURN_ANIM_SPEED;
  6381. }
  6382. else
  6383. {
  6384. SetSoldierAniSpeed( pSoldier );
  6385. // FreeUpNPCFromTurning( pSoldier, LOOK);
  6386. }
  6387. }
  6388. }
  6389. BOOLEAN IsActionInterruptable( SOLDIERTYPE *pSoldier )
  6390. {
  6391. if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_NONINTERRUPT )
  6392. {
  6393. return( FALSE );
  6394. }
  6395. return( TRUE );
  6396. }
  6397. // WRAPPER FUNCTIONS FOR SOLDIER EVENTS
  6398. void SendSoldierPositionEvent( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos )
  6399. {
  6400. // Sent event for position update
  6401. EV_S_SETPOSITION SSetPosition;
  6402. SSetPosition.usSoldierID = pSoldier->ubID;
  6403. SSetPosition.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
  6404. SSetPosition.dNewXPos = dNewXPos;
  6405. SSetPosition.dNewYPos = dNewYPos;
  6406. AddGameEvent( S_SETPOSITION, 0, &SSetPosition );
  6407. }
  6408. void SendSoldierDestinationEvent( SOLDIERTYPE *pSoldier, UINT16 usNewDestination )
  6409. {
  6410. // Sent event for position update
  6411. EV_S_CHANGEDEST SChangeDest;
  6412. SChangeDest.usSoldierID = pSoldier->ubID;
  6413. SChangeDest.usNewDestination = usNewDestination;
  6414. SChangeDest.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
  6415. AddGameEvent( S_CHANGEDEST, 0, &SChangeDest );
  6416. }
  6417. void SendSoldierSetDirectionEvent( SOLDIERTYPE *pSoldier, UINT16 usNewDirection )
  6418. {
  6419. // Sent event for position update
  6420. EV_S_SETDIRECTION SSetDirection;
  6421. SSetDirection.usSoldierID = pSoldier->ubID;
  6422. SSetDirection.usNewDirection = usNewDirection;
  6423. SSetDirection.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
  6424. AddGameEvent( S_SETDIRECTION, 0, &SSetDirection );
  6425. }
  6426. void SendSoldierSetDesiredDirectionEvent( SOLDIERTYPE *pSoldier, UINT16 usDesiredDirection )
  6427. {
  6428. // Sent event for position update
  6429. EV_S_SETDESIREDDIRECTION SSetDesiredDirection;
  6430. SSetDesiredDirection.usSoldierID = pSoldier->ubID;
  6431. SSetDesiredDirection.usDesiredDirection = usDesiredDirection;
  6432. SSetDesiredDirection.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
  6433. AddGameEvent( S_SETDESIREDDIRECTION, 0, &SSetDesiredDirection );
  6434. }
  6435. void SendGetNewSoldierPathEvent( SOLDIERTYPE *pSoldier, UINT16 sDestGridNo, UINT16 usMovementAnim )
  6436. {
  6437. EV_S_GETNEWPATH SGetNewPath;
  6438. SGetNewPath.usSoldierID = pSoldier->ubID;
  6439. SGetNewPath.sDestGridNo = sDestGridNo;
  6440. SGetNewPath.usMovementAnim = usMovementAnim;
  6441. SGetNewPath.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
  6442. AddGameEvent( S_GETNEWPATH, 0, &SGetNewPath );
  6443. }
  6444. void SendChangeSoldierStanceEvent( SOLDIERTYPE *pSoldier, UINT8 ubNewStance )
  6445. {
  6446. #if 0
  6447. EV_S_CHANGESTANCE SChangeStance;
  6448. #ifdef NETWORKED
  6449. if( !IsTheSolderUnderMyControl( pSoldier->ubID) )
  6450. return;
  6451. #endif
  6452. SChangeStance.ubNewStance = ubNewStance;
  6453. SChangeStance.usSoldierID = pSoldier->ubID;
  6454. SChangeStance.sXPos = pSoldier->sX;
  6455. SChangeStance.sYPos = pSoldier->sY;
  6456. SChangeStance.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
  6457. AddGameEvent( S_CHANGESTANCE, 0, &SChangeStance );
  6458. #endif
  6459. ChangeSoldierStance( pSoldier, ubNewStance );
  6460. }
  6461. void SendBeginFireWeaponEvent( SOLDIERTYPE *pSoldier, INT16 sTargetGridNo )
  6462. {
  6463. EV_S_BEGINFIREWEAPON SBeginFireWeapon;
  6464. SBeginFireWeapon.usSoldierID = pSoldier->ubID;
  6465. SBeginFireWeapon.sTargetGridNo = sTargetGridNo;
  6466. SBeginFireWeapon.bTargetLevel = pSoldier->bTargetLevel;
  6467. SBeginFireWeapon.bTargetCubeLevel = pSoldier->bTargetCubeLevel;
  6468. SBeginFireWeapon.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
  6469. AddGameEvent( S_BEGINFIREWEAPON, 0, &SBeginFireWeapon );
  6470. }
  6471. // This function just encapolates the check for turnbased and having an attacker in the first place
  6472. void ReleaseSoldiersAttacker( SOLDIERTYPE *pSoldier )
  6473. {
  6474. INT32 cnt;
  6475. UINT8 ubNumToFree;
  6476. //if ( gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT) )
  6477. {
  6478. // ATE: Removed...
  6479. //if ( pSoldier->ubAttackerID != NOBODY )
  6480. {
  6481. // JA2 Gold
  6482. // set next-to-previous attacker, so long as this isn't a repeat attack
  6483. if (pSoldier->ubPreviousAttackerID != pSoldier->ubAttackerID)
  6484. {
  6485. pSoldier->ubNextToPreviousAttackerID = pSoldier->ubPreviousAttackerID;
  6486. }
  6487. // get previous attacker id
  6488. pSoldier->ubPreviousAttackerID = pSoldier->ubAttackerID;
  6489. // Copy BeingAttackedCount here....
  6490. ubNumToFree = pSoldier->bBeingAttackedCount;
  6491. // Zero it out BEFORE, as supression may increase it again...
  6492. pSoldier->bBeingAttackedCount = 0;
  6493. for ( cnt = 0; cnt < ubNumToFree; cnt++ )
  6494. {
  6495. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker of %d (attacker is %d) - releasesoldierattacker num to free is %d", pSoldier->ubID, pSoldier->ubAttackerID, ubNumToFree ) );
  6496. ReduceAttackBusyCount( pSoldier->ubAttackerID, FALSE );
  6497. }
  6498. // ATE: Set to NOBODY if this person is NOT dead
  6499. // otherise, we keep it so the kill can be awarded!
  6500. if ( pSoldier->bLife != 0 && pSoldier->ubBodyType != QUEENMONSTER )
  6501. {
  6502. pSoldier->ubAttackerID = NOBODY;
  6503. }
  6504. }
  6505. }
  6506. }
  6507. BOOLEAN MercInWater( SOLDIERTYPE *pSoldier )
  6508. {
  6509. // Our water texture , for now is of a given type
  6510. if ( pSoldier->bOverTerrainType == LOW_WATER || pSoldier->bOverTerrainType == MED_WATER || pSoldier->bOverTerrainType == DEEP_WATER )
  6511. {
  6512. return( TRUE );
  6513. }
  6514. else
  6515. {
  6516. return( FALSE );
  6517. }
  6518. }
  6519. void RevivePlayerTeam( )
  6520. {
  6521. INT32 cnt;
  6522. SOLDIERTYPE *pSoldier;
  6523. // End the turn of player charactors
  6524. cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
  6525. // look for all mercs on the same team,
  6526. for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pSoldier++)
  6527. {
  6528. ReviveSoldier( pSoldier );
  6529. }
  6530. }
  6531. void ReviveSoldier( SOLDIERTYPE *pSoldier )
  6532. {
  6533. INT16 sX, sY;
  6534. if ( pSoldier->bLife < OKLIFE && pSoldier->bActive )
  6535. {
  6536. // If dead or unconscious, revive!
  6537. pSoldier->uiStatusFlags &= ( ~SOLDIER_DEAD );
  6538. pSoldier->bLife = pSoldier->bLifeMax;
  6539. pSoldier->bBleeding = 0;
  6540. pSoldier->ubDesiredHeight = ANIM_STAND;
  6541. AddManToTeam( pSoldier->bTeam );
  6542. // Set to standing
  6543. pSoldier->fInNonintAnim = FALSE;
  6544. pSoldier->fRTInNonintAnim = FALSE;
  6545. // Change to standing,unless we can getup with an animation
  6546. EVENT_InitNewSoldierAnim( pSoldier, STANDING, 0, TRUE );
  6547. BeginSoldierGetup( pSoldier );
  6548. // Makesure center of tile
  6549. sX = CenterX( pSoldier->sGridNo );
  6550. sY = CenterY( pSoldier->sGridNo );
  6551. EVENT_SetSoldierPosition( pSoldier, (FLOAT) sX, (FLOAT) sY );
  6552. // Dirty INterface
  6553. fInterfacePanelDirty = DIRTYLEVEL2;
  6554. }
  6555. }
  6556. void HandleAnimationProfile( SOLDIERTYPE *pSoldier, UINT16 usAnimState, BOOLEAN fRemove )
  6557. {
  6558. //#if 0
  6559. ANIM_PROF *pProfile;
  6560. ANIM_PROF_DIR *pProfileDir;
  6561. ANIM_PROF_TILE *pProfileTile;
  6562. INT8 bProfileID;
  6563. UINT32 iTileCount;
  6564. INT16 sGridNo;
  6565. UINT16 usAnimSurface;
  6566. // ATE
  6567. // Get Surface Index
  6568. usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, usAnimState );
  6569. CHECKV( usAnimSurface != INVALID_ANIMATION_SURFACE );
  6570. bProfileID = gAnimSurfaceDatabase[ usAnimSurface ].bProfile;
  6571. // Determine if this animation has a profile
  6572. if ( bProfileID != -1 )
  6573. {
  6574. // Getprofile
  6575. pProfile = &(gpAnimProfiles[ bProfileID ] );
  6576. // Get direction
  6577. pProfileDir = &( pProfile->Dirs[ pSoldier->bDirection ] );
  6578. // Loop tiles and set accordingly into world
  6579. for( iTileCount = 0; iTileCount < pProfileDir->ubNumTiles; iTileCount++ )
  6580. {
  6581. pProfileTile = &( pProfileDir->pTiles[ iTileCount ] );
  6582. sGridNo = pSoldier->sGridNo + ( ( WORLD_COLS * pProfileTile->bTileY ) + pProfileTile->bTileX );
  6583. // Check if in bounds
  6584. if ( !OutOfBounds( pSoldier->sGridNo, sGridNo ) )
  6585. {
  6586. if ( fRemove )
  6587. {
  6588. // Remove from world
  6589. RemoveMerc( sGridNo, pSoldier, TRUE );
  6590. }
  6591. else
  6592. {
  6593. // PLace into world
  6594. AddMercToHead( sGridNo, pSoldier, FALSE );
  6595. //if ( pProfileTile->bTileY != 0 || pProfileTile->bTileX != 0 )
  6596. {
  6597. gpWorldLevelData[sGridNo].pMercHead->uiFlags |= LEVELNODE_MERCPLACEHOLDER;
  6598. gpWorldLevelData[sGridNo].pMercHead->uiAnimHitLocationFlags = pProfileTile->usTileFlags;
  6599. }
  6600. }
  6601. }
  6602. }
  6603. }
  6604. //#endif
  6605. }
  6606. LEVELNODE *GetAnimProfileFlags( UINT16 sGridNo, UINT16 *usFlags, SOLDIERTYPE **ppTargSoldier, LEVELNODE *pGivenNode )
  6607. {
  6608. LEVELNODE *pNode;
  6609. (*ppTargSoldier) = NULL;
  6610. (*usFlags ) = 0;
  6611. if ( pGivenNode == NULL )
  6612. {
  6613. pNode = gpWorldLevelData[sGridNo].pMercHead;
  6614. }
  6615. else
  6616. {
  6617. pNode = pGivenNode->pNext;
  6618. }
  6619. //#if 0
  6620. if ( pNode != NULL )
  6621. {
  6622. if ( pNode->uiFlags & LEVELNODE_MERCPLACEHOLDER )
  6623. {
  6624. (*usFlags) = (UINT16)pNode->uiAnimHitLocationFlags;
  6625. (*ppTargSoldier) = pNode->pSoldier;
  6626. }
  6627. }
  6628. //#endif
  6629. return( pNode );
  6630. }
  6631. BOOLEAN GetProfileFlagsFromGridno( SOLDIERTYPE *pSoldier, UINT16 usAnimState, UINT16 sTestGridNo, UINT16 *usFlags )
  6632. {
  6633. ANIM_PROF *pProfile;
  6634. ANIM_PROF_DIR *pProfileDir;
  6635. ANIM_PROF_TILE *pProfileTile;
  6636. INT8 bProfileID;
  6637. UINT32 iTileCount;
  6638. INT16 sGridNo;
  6639. UINT16 usAnimSurface;
  6640. // Get Surface Index
  6641. usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, usAnimState );
  6642. CHECKF( usAnimSurface != INVALID_ANIMATION_SURFACE );
  6643. bProfileID = gAnimSurfaceDatabase[ usAnimSurface ].bProfile;
  6644. *usFlags = 0;
  6645. // Determine if this animation has a profile
  6646. if ( bProfileID != -1 )
  6647. {
  6648. // Getprofile
  6649. pProfile = &(gpAnimProfiles[ bProfileID ] );
  6650. // Get direction
  6651. pProfileDir = &( pProfile->Dirs[ pSoldier->bDirection ] );
  6652. // Loop tiles and set accordingly into world
  6653. for( iTileCount = 0; iTileCount < pProfileDir->ubNumTiles; iTileCount++ )
  6654. {
  6655. pProfileTile = &( pProfileDir->pTiles[ iTileCount ] );
  6656. sGridNo = pSoldier->sGridNo + ( ( WORLD_COLS * pProfileTile->bTileY ) + pProfileTile->bTileX );
  6657. // Check if in bounds
  6658. if ( !OutOfBounds( pSoldier->sGridNo, sGridNo ) )
  6659. {
  6660. if ( sGridNo == sTestGridNo )
  6661. {
  6662. *usFlags = pProfileTile->usTileFlags;
  6663. return( TRUE );
  6664. }
  6665. }
  6666. }
  6667. }
  6668. return( FALSE );
  6669. }
  6670. void EVENT_SoldierBeginGiveItem( SOLDIERTYPE *pSoldier )
  6671. {
  6672. SOLDIERTYPE *pTSoldier;
  6673. if ( VerifyGiveItem( pSoldier, &pTSoldier ) )
  6674. {
  6675. // CHANGE DIRECTION AND GOTO ANIMATION NOW
  6676. pSoldier->bDesiredDirection = pSoldier->bPendingActionData3;
  6677. pSoldier->bDirection = pSoldier->bPendingActionData3;
  6678. // begin animation
  6679. EVENT_InitNewSoldierAnim( pSoldier, GIVE_ITEM, 0 , FALSE );
  6680. }
  6681. else
  6682. {
  6683. UnSetEngagedInConvFromPCAction( pSoldier );
  6684. MemFree( pSoldier->pTempObject );
  6685. }
  6686. }
  6687. void EVENT_SoldierBeginBladeAttack( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
  6688. {
  6689. SOLDIERTYPE *pTSoldier;
  6690. //UINT32 uiMercFlags;
  6691. UINT16 usSoldierIndex;
  6692. UINT8 ubTDirection;
  6693. BOOLEAN fChangeDirection = FALSE;
  6694. ROTTING_CORPSE *pCorpse;
  6695. // Increment the number of people busy doing stuff because of an attack
  6696. //if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) )
  6697. //{
  6698. gTacticalStatus.ubAttackBusyCount++;
  6699. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Begin blade attack: ATB %d", gTacticalStatus.ubAttackBusyCount) );
  6700. //}
  6701. // CHANGE DIRECTION AND GOTO ANIMATION NOW
  6702. EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
  6703. EVENT_SetSoldierDirection( pSoldier, ubDirection );
  6704. // CHANGE TO ANIMATION
  6705. // DETERMINE ANIMATION TO PLAY
  6706. // LATER BASED ON IF TAREGT KNOWS OF US, STANCE, ETC
  6707. // GET POINTER TO TAREGT
  6708. if (pSoldier->uiStatusFlags & SOLDIER_MONSTER)
  6709. {
  6710. UINT8 ubTargetID;
  6711. // Is there an unconscious guy at gridno......
  6712. ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel );
  6713. if ( ubTargetID != NOBODY && ( ( MercPtrs[ ubTargetID ]->bLife < OKLIFE && MercPtrs[ ubTargetID ]->bLife > 0 ) || ( MercPtrs[ ubTargetID ]->bBreath < OKBREATH && MercPtrs[ ubTargetID ]->bCollapsed ) ) )
  6714. {
  6715. pSoldier->uiPendingActionData4 = ubTargetID;
  6716. // add regen bonus
  6717. pSoldier->bRegenerationCounter++;
  6718. EVENT_InitNewSoldierAnim( pSoldier, MONSTER_BEGIN_EATTING_FLESH, 0, FALSE );
  6719. }
  6720. else
  6721. {
  6722. if ( PythSpacesAway( pSoldier->sGridNo, sGridNo ) <= 1 )
  6723. {
  6724. EVENT_InitNewSoldierAnim( pSoldier, MONSTER_CLOSE_ATTACK, 0, FALSE );
  6725. }
  6726. else
  6727. {
  6728. EVENT_InitNewSoldierAnim( pSoldier, ADULTMONSTER_ATTACKING, 0, FALSE );
  6729. }
  6730. }
  6731. }
  6732. else if (pSoldier->ubBodyType == BLOODCAT)
  6733. {
  6734. // Check if it's a claws or teeth...
  6735. if ( pSoldier->inv[ HANDPOS ].usItem == BLOODCAT_CLAW_ATTACK )
  6736. {
  6737. EVENT_InitNewSoldierAnim( pSoldier, BLOODCAT_SWIPE, 0, FALSE );
  6738. }
  6739. else
  6740. {
  6741. EVENT_InitNewSoldierAnim( pSoldier, BLOODCAT_BITE_ANIM, 0, FALSE );
  6742. }
  6743. }
  6744. else
  6745. {
  6746. usSoldierIndex = WhoIsThere2( sGridNo, pSoldier->bTargetLevel );
  6747. if ( usSoldierIndex != NOBODY )
  6748. {
  6749. GetSoldier( &pTSoldier, usSoldierIndex );
  6750. // Look at stance of target
  6751. switch( gAnimControl[ pTSoldier->usAnimState ].ubEndHeight )
  6752. {
  6753. case ANIM_STAND:
  6754. case ANIM_CROUCH:
  6755. // CHECK IF HE CAN SEE US, IF SO RANDOMIZE
  6756. if ( pTSoldier->bOppList[ pSoldier->ubID ] == 0 && pTSoldier->bTeam != pSoldier->bTeam )
  6757. {
  6758. // WE ARE NOT SEEN
  6759. EVENT_InitNewSoldierAnim( pSoldier, STAB, 0 , FALSE );
  6760. }
  6761. else
  6762. {
  6763. // WE ARE SEEN
  6764. if ( Random( 50 ) > 25 )
  6765. {
  6766. EVENT_InitNewSoldierAnim( pSoldier, STAB, 0 , FALSE );
  6767. }
  6768. else
  6769. {
  6770. EVENT_InitNewSoldierAnim( pSoldier, SLICE, 0 , FALSE );
  6771. }
  6772. // IF WE ARE SEEN, MAKE SURE GUY TURNS!
  6773. // Get direction to target
  6774. // IF WE ARE AN ANIMAL, CAR, MONSTER, DONT'T TURN
  6775. if ( !( pTSoldier->uiStatusFlags & ( SOLDIER_MONSTER | SOLDIER_ANIMAL | SOLDIER_VEHICLE ) ) )
  6776. {
  6777. // OK, stop merc....
  6778. EVENT_StopMerc( pTSoldier, pTSoldier->sGridNo, pTSoldier->bDirection );
  6779. if ( pTSoldier->bTeam != gbPlayerNum )
  6780. {
  6781. CancelAIAction( pTSoldier, TRUE );
  6782. }
  6783. ubTDirection = (UINT8)GetDirectionFromGridNo( pSoldier->sGridNo, pTSoldier );
  6784. SendSoldierSetDesiredDirectionEvent( pTSoldier, ubTDirection );
  6785. }
  6786. }
  6787. break;
  6788. case ANIM_PRONE:
  6789. // CHECK OUR STANCE
  6790. if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_CROUCH )
  6791. {
  6792. // SET DESIRED STANCE AND SET PENDING ANIMATION
  6793. SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH );
  6794. pSoldier->usPendingAnimation = CROUCH_STAB;
  6795. }
  6796. else
  6797. {
  6798. // USE crouched one
  6799. // NEED TO CHANGE STANCE IF NOT CROUCHD!
  6800. EVENT_InitNewSoldierAnim( pSoldier, CROUCH_STAB, 0 , FALSE );
  6801. }
  6802. break;
  6803. }
  6804. }
  6805. else
  6806. {
  6807. // OK, SEE IF THERE IS AN OBSTACLE HERE...
  6808. if ( !NewOKDestination( pSoldier, sGridNo, FALSE, pSoldier->bLevel ) )
  6809. {
  6810. EVENT_InitNewSoldierAnim( pSoldier, STAB, 0 , FALSE );
  6811. }
  6812. else
  6813. {
  6814. // Check for corpse!
  6815. pCorpse = GetCorpseAtGridNo( sGridNo, pSoldier->bLevel );
  6816. if ( pCorpse == NULL )
  6817. {
  6818. EVENT_InitNewSoldierAnim( pSoldier, CROUCH_STAB, 0 , FALSE );
  6819. }
  6820. else
  6821. {
  6822. if ( IsValidDecapitationCorpse( pCorpse ) )
  6823. {
  6824. EVENT_InitNewSoldierAnim( pSoldier, DECAPITATE, 0 , FALSE );
  6825. }
  6826. else
  6827. {
  6828. EVENT_InitNewSoldierAnim( pSoldier, CROUCH_STAB, 0 , FALSE );
  6829. }
  6830. }
  6831. }
  6832. }
  6833. }
  6834. // SET TARGET GRIDNO
  6835. pSoldier->sTargetGridNo = sGridNo;
  6836. pSoldier->bTargetLevel = pSoldier->bLevel;
  6837. pSoldier->ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel );
  6838. }
  6839. void EVENT_SoldierBeginPunchAttack( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
  6840. {
  6841. BOOLEAN fMartialArtist = FALSE;
  6842. SOLDIERTYPE *pTSoldier;
  6843. //UINT32 uiMercFlags;
  6844. UINT16 usSoldierIndex;
  6845. UINT8 ubTDirection;
  6846. BOOLEAN fChangeDirection = FALSE;
  6847. UINT16 usItem;
  6848. // Get item in hand...
  6849. usItem = pSoldier->inv[ HANDPOS ].usItem;
  6850. // Increment the number of people busy doing stuff because of an attack
  6851. //if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) )
  6852. //{
  6853. gTacticalStatus.ubAttackBusyCount++;
  6854. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Begin HTH attack: ATB %d", gTacticalStatus.ubAttackBusyCount) );
  6855. //}
  6856. // get target.....
  6857. usSoldierIndex = WhoIsThere2( pSoldier->sTargetGridNo, pSoldier->bLevel );
  6858. if ( usSoldierIndex != NOBODY )
  6859. {
  6860. GetSoldier( &pTSoldier, usSoldierIndex );
  6861. fChangeDirection = TRUE;
  6862. }
  6863. else
  6864. {
  6865. return;
  6866. }
  6867. if ( fChangeDirection )
  6868. {
  6869. // CHANGE DIRECTION AND GOTO ANIMATION NOW
  6870. EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
  6871. EVENT_SetSoldierDirection( pSoldier, ubDirection );
  6872. }
  6873. // Are we a martial artist?
  6874. if ( HAS_SKILL_TRAIT( pSoldier, MARTIALARTS ) )
  6875. {
  6876. fMartialArtist = TRUE;
  6877. }
  6878. if ( fMartialArtist && !AreInMeanwhile( ) && usItem != CROWBAR )
  6879. {
  6880. // Are we in attack mode yet?
  6881. if ( pSoldier->usAnimState != NINJA_BREATH && gAnimControl[ pSoldier->usAnimState ].ubHeight == ANIM_STAND && gAnimControl[ pTSoldier->usAnimState ].ubHeight != ANIM_PRONE )
  6882. {
  6883. EVENT_InitNewSoldierAnim( pSoldier, NINJA_GOTOBREATH, 0 , FALSE );
  6884. }
  6885. else
  6886. {
  6887. DoNinjaAttack( pSoldier );
  6888. }
  6889. }
  6890. else
  6891. {
  6892. // Look at stance of target
  6893. switch( gAnimControl[ pTSoldier->usAnimState ].ubEndHeight )
  6894. {
  6895. case ANIM_STAND:
  6896. case ANIM_CROUCH:
  6897. if ( usItem != CROWBAR )
  6898. {
  6899. EVENT_InitNewSoldierAnim( pSoldier, PUNCH, 0 , FALSE );
  6900. }
  6901. else
  6902. {
  6903. EVENT_InitNewSoldierAnim( pSoldier, CROWBAR_ATTACK, 0 , FALSE );
  6904. }
  6905. // CHECK IF HE CAN SEE US, IF SO CHANGE DIR
  6906. if ( pTSoldier->bOppList[ pSoldier->ubID ] == 0 && pTSoldier->bTeam != pSoldier->bTeam )
  6907. {
  6908. // Get direction to target
  6909. // IF WE ARE AN ANIMAL, CAR, MONSTER, DONT'T TURN
  6910. if ( !( pTSoldier->uiStatusFlags & ( SOLDIER_MONSTER | SOLDIER_ANIMAL | SOLDIER_VEHICLE ) ) )
  6911. {
  6912. // OK, stop merc....
  6913. EVENT_StopMerc( pTSoldier, pTSoldier->sGridNo, pTSoldier->bDirection );
  6914. if ( pTSoldier->bTeam != gbPlayerNum )
  6915. {
  6916. CancelAIAction( pTSoldier, TRUE );
  6917. }
  6918. ubTDirection = (UINT8)GetDirectionFromGridNo( pSoldier->sGridNo, pTSoldier );
  6919. SendSoldierSetDesiredDirectionEvent( pTSoldier, ubTDirection );
  6920. }
  6921. }
  6922. break;
  6923. case ANIM_PRONE:
  6924. // CHECK OUR STANCE
  6925. // ATE: Added this for CIV body types 'cause of elliot
  6926. if ( !IS_MERC_BODY_TYPE( pSoldier ) )
  6927. {
  6928. EVENT_InitNewSoldierAnim( pSoldier, PUNCH, 0 , FALSE );
  6929. }
  6930. else
  6931. {
  6932. if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_CROUCH )
  6933. {
  6934. // SET DESIRED STANCE AND SET PENDING ANIMATION
  6935. SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH );
  6936. pSoldier->usPendingAnimation = PUNCH_LOW;
  6937. }
  6938. else
  6939. {
  6940. // USE crouched one
  6941. // NEED TO CHANGE STANCE IF NOT CROUCHD!
  6942. EVENT_InitNewSoldierAnim( pSoldier, PUNCH_LOW, 0 , FALSE );
  6943. }
  6944. }
  6945. break;
  6946. }
  6947. }
  6948. // SET TARGET GRIDNO
  6949. pSoldier->sTargetGridNo = sGridNo;
  6950. pSoldier->bTargetLevel = pSoldier->bLevel;
  6951. pSoldier->sLastTarget = sGridNo;
  6952. pSoldier->ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel );
  6953. }
  6954. void EVENT_SoldierBeginKnifeThrowAttack( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
  6955. {
  6956. // Increment the number of people busy doing stuff because of an attack
  6957. //if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) )
  6958. //{
  6959. gTacticalStatus.ubAttackBusyCount++;
  6960. //}
  6961. pSoldier->bBulletsLeft = 1;
  6962. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting knifethrow attack, bullets left %d", pSoldier->bBulletsLeft) );
  6963. EVENT_InitNewSoldierAnim( pSoldier, THROW_KNIFE, 0 , FALSE );
  6964. // CHANGE DIRECTION AND GOTO ANIMATION NOW
  6965. EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
  6966. EVENT_SetSoldierDirection( pSoldier, ubDirection );
  6967. // SET TARGET GRIDNO
  6968. pSoldier->sTargetGridNo = sGridNo;
  6969. pSoldier->sLastTarget = sGridNo;
  6970. pSoldier->fTurningFromPronePosition = 0;
  6971. // NB target level must be set by functions outside of here... but I think it
  6972. // is already set in HandleItem or in the AI code - CJC
  6973. pSoldier->ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel );
  6974. }
  6975. void EVENT_SoldierBeginDropBomb( SOLDIERTYPE *pSoldier )
  6976. {
  6977. // Increment the number of people busy doing stuff because of an attack
  6978. switch( gAnimControl[ pSoldier->usAnimState ].ubHeight )
  6979. {
  6980. case ANIM_STAND:
  6981. EVENT_InitNewSoldierAnim( pSoldier, PLANT_BOMB, 0 , FALSE );
  6982. break;
  6983. default:
  6984. // Call hander for planting bomb...
  6985. HandleSoldierDropBomb( pSoldier, pSoldier->sPendingActionData2 );
  6986. SoldierGotoStationaryStance( pSoldier );
  6987. break;
  6988. }
  6989. }
  6990. void EVENT_SoldierBeginUseDetonator( SOLDIERTYPE *pSoldier )
  6991. {
  6992. // Increment the number of people busy doing stuff because of an attack
  6993. switch( gAnimControl[ pSoldier->usAnimState ].ubHeight )
  6994. {
  6995. case ANIM_STAND:
  6996. EVENT_InitNewSoldierAnim( pSoldier, USE_REMOTE, 0 , FALSE );
  6997. break;
  6998. default:
  6999. // Call hander for planting bomb...
  7000. HandleSoldierUseRemote( pSoldier, pSoldier->sPendingActionData2 );
  7001. break;
  7002. }
  7003. }
  7004. void EVENT_SoldierBeginFirstAid( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
  7005. {
  7006. SOLDIERTYPE *pTSoldier;
  7007. //UINT32 uiMercFlags;
  7008. UINT16 usSoldierIndex;
  7009. BOOLEAN fRefused = FALSE;
  7010. usSoldierIndex = WhoIsThere2( sGridNo, pSoldier->bLevel );
  7011. if ( usSoldierIndex != NOBODY )
  7012. {
  7013. pTSoldier = MercPtrs[ usSoldierIndex ];
  7014. // OK, check if we should play quote...
  7015. if ( pTSoldier->bTeam != gbPlayerNum )
  7016. {
  7017. if ( pTSoldier->ubProfile != NO_PROFILE && pTSoldier->ubProfile >= FIRST_RPC && !RPC_RECRUITED( pTSoldier ) )
  7018. {
  7019. fRefused = PCDoesFirstAidOnNPC( pTSoldier->ubProfile );
  7020. }
  7021. if ( !fRefused )
  7022. {
  7023. if ( CREATURE_OR_BLOODCAT( pTSoldier ) )
  7024. {
  7025. // nope!!
  7026. fRefused = TRUE;
  7027. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, Message[ STR_REFUSE_FIRSTAID_FOR_CREATURE ] );
  7028. }
  7029. else if ( !pTSoldier->bNeutral && pTSoldier->bLife >= OKLIFE && pTSoldier->bSide != pSoldier->bSide )
  7030. {
  7031. fRefused = TRUE;
  7032. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, Message[ STR_REFUSE_FIRSTAID ] );
  7033. }
  7034. }
  7035. }
  7036. if ( fRefused )
  7037. {
  7038. UnSetUIBusy( pSoldier->ubID );
  7039. return;
  7040. }
  7041. // ATE: We can only give firsty aid to one perosn at a time... cancel
  7042. // any now...
  7043. InternalGivingSoldierCancelServices( pSoldier, FALSE );
  7044. // CHANGE DIRECTION AND GOTO ANIMATION NOW
  7045. EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
  7046. EVENT_SetSoldierDirection( pSoldier, ubDirection );
  7047. // CHECK OUR STANCE AND GOTO CROUCH IF NEEDED
  7048. //if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_CROUCH )
  7049. //{
  7050. // SET DESIRED STANCE AND SET PENDING ANIMATION
  7051. // SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH );
  7052. // pSoldier->usPendingAnimation = START_AID;
  7053. //}
  7054. //else
  7055. {
  7056. // CHANGE TO ANIMATION
  7057. EVENT_InitNewSoldierAnim( pSoldier, START_AID, 0 , FALSE );
  7058. }
  7059. // SET TARGET GRIDNO
  7060. pSoldier->sTargetGridNo = sGridNo;
  7061. // SET PARTNER ID
  7062. pSoldier->ubServicePartner = (UINT8)usSoldierIndex;
  7063. // SET PARTNER'S COUNT REFERENCE
  7064. pTSoldier->ubServiceCount++;
  7065. // If target and doer are no the same guy...
  7066. if ( pTSoldier->ubID != pSoldier->ubID && !pTSoldier->bCollapsed )
  7067. {
  7068. SoldierGotoStationaryStance( pTSoldier );
  7069. }
  7070. }
  7071. }
  7072. void EVENT_SoldierEnterVehicle( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
  7073. {
  7074. SOLDIERTYPE *pTSoldier;
  7075. UINT32 uiMercFlags;
  7076. UINT16 usSoldierIndex;
  7077. if ( FindSoldier( sGridNo, &usSoldierIndex, &uiMercFlags, FIND_SOLDIER_GRIDNO ) )
  7078. {
  7079. pTSoldier = MercPtrs[ usSoldierIndex ];
  7080. // Enter vehicle...
  7081. EnterVehicle( pTSoldier, pSoldier );
  7082. }
  7083. UnSetUIBusy( pSoldier->ubID );
  7084. }
  7085. UINT32 SoldierDressWound( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pVictim, INT16 sKitPts, INT16 sStatus )
  7086. {
  7087. UINT32 uiDressSkill, uiPossible, uiActual, uiMedcost, uiDeficiency, uiAvailAPs, uiUsedAPs;
  7088. UINT8 ubBelowOKlife, ubPtsLeft;
  7089. BOOLEAN fRanOut = FALSE;
  7090. if (pVictim->bBleeding < 1 && pVictim->bLife >= OKLIFE )
  7091. {
  7092. return(0); // nothing to do, shouldn't have even been called!
  7093. }
  7094. if ( pVictim->bLife == 0 )
  7095. {
  7096. return(0);
  7097. }
  7098. // in case he has multiple kits in hand, limit influence of kit status to 100%!
  7099. if (sStatus >= 100)
  7100. {
  7101. sStatus = 100;
  7102. }
  7103. // calculate wound-dressing skill (3x medical, 2x equip, 1x level, 1x dex)
  7104. uiDressSkill = ( ( 3 * EffectiveMedical( pSoldier ) ) + // medical knowledge
  7105. ( 2 * sStatus) + // state of medical kit
  7106. (10 * EffectiveExpLevel( pSoldier ) ) + // battle injury experience
  7107. EffectiveDexterity( pSoldier ) ) / 7; // general "handiness"
  7108. // try to use every AP that the merc has left
  7109. uiAvailAPs = pSoldier->bActionPoints;
  7110. // OK, If we are in real-time, use another value...
  7111. if (!(gTacticalStatus.uiFlags & TURNBASED) || !(gTacticalStatus.uiFlags & INCOMBAT ) )
  7112. {
  7113. // Set to a value which looks good based on our tactical turns duration
  7114. uiAvailAPs = RT_FIRST_AID_GAIN_MODIFIER;
  7115. }
  7116. // calculate how much bandaging CAN be done this turn
  7117. uiPossible = ( uiAvailAPs * uiDressSkill ) / 50; // max rate is 2 * fullAPs
  7118. // if no healing is possible (insufficient APs or insufficient dressSkill)
  7119. if (!uiPossible)
  7120. return(0);
  7121. if (pSoldier->inv[ HANDPOS ].usItem == MEDICKIT ) // using the GOOD medic stuff
  7122. {
  7123. uiPossible += ( uiPossible / 2); // add extra 50 %
  7124. }
  7125. uiActual = uiPossible; // start by assuming maximum possible
  7126. // figure out how far below OKLIFE the victim is
  7127. if (pVictim->bLife >= OKLIFE)
  7128. {
  7129. ubBelowOKlife = 0;
  7130. }
  7131. else
  7132. {
  7133. ubBelowOKlife = OKLIFE - pVictim->bLife;
  7134. }
  7135. // figure out how many healing pts we need to stop dying (2x cost)
  7136. uiDeficiency = (2 * ubBelowOKlife );
  7137. // if, after that, the patient will still be bleeding
  7138. if ( (pVictim->bBleeding - ubBelowOKlife ) > 0)
  7139. {
  7140. // then add how many healing pts we need to stop bleeding (1x cost)
  7141. uiDeficiency += ( pVictim->bBleeding - ubBelowOKlife );
  7142. }
  7143. // now, make sure we weren't going to give too much
  7144. if ( uiActual > uiDeficiency) // if we were about to apply too much
  7145. uiActual = uiDeficiency; // reduce actual not to waste anything
  7146. // now make sure we HAVE that much
  7147. if (pSoldier->inv[ HANDPOS ].usItem == MEDICKIT)
  7148. {
  7149. uiMedcost = (uiActual + 1) / 2; // cost is only half, rounded up
  7150. if ( uiMedcost > (UINT32)sKitPts ) // if we can't afford this
  7151. {
  7152. fRanOut = TRUE;
  7153. uiMedcost = sKitPts; // what CAN we afford?
  7154. uiActual = uiMedcost * 2; // give double this as aid
  7155. }
  7156. }
  7157. else
  7158. {
  7159. uiMedcost = uiActual;
  7160. if ( uiMedcost > (UINT32)sKitPts) // can't afford it
  7161. {
  7162. fRanOut = TRUE;
  7163. uiMedcost = uiActual = sKitPts; // recalc cost AND aid
  7164. }
  7165. }
  7166. ubPtsLeft = (UINT8)uiActual;
  7167. // heal real life points first (if below OKLIFE) because we don't want the
  7168. // patient still DYING if bandages run out, or medic is disabled/distracted!
  7169. // NOTE: Dressing wounds for life below OKLIFE now costs 2 pts/life point!
  7170. if ( ubPtsLeft && pVictim->bLife < OKLIFE)
  7171. {
  7172. // if we have enough points to bring him all the way to OKLIFE this turn
  7173. if ( ubPtsLeft >= (2 * ubBelowOKlife ) )
  7174. {
  7175. // raise life to OKLIFE
  7176. pVictim->bLife = OKLIFE;
  7177. // reduce bleeding by the same number of life points healed up
  7178. pVictim->bBleeding -= ubBelowOKlife;
  7179. // use up appropriate # of actual healing points
  7180. ubPtsLeft -= (2 * ubBelowOKlife);
  7181. }
  7182. else
  7183. {
  7184. pVictim->bLife += ( ubPtsLeft / 2);
  7185. pVictim->bBleeding -= ( ubPtsLeft / 2);
  7186. ubPtsLeft = ubPtsLeft % 2; // if ptsLeft was odd, ptsLeft = 1
  7187. }
  7188. // this should never happen any more, but make sure bleeding not negative
  7189. if (pVictim->bBleeding < 0)
  7190. {
  7191. pVictim->bBleeding = 0;
  7192. }
  7193. // if this healing brought the patient out of the worst of it, cancel dying
  7194. if (pVictim->bLife >= OKLIFE )
  7195. {
  7196. //pVictim->dying = pVictim->dyingComment = FALSE;
  7197. //pVictim->shootOn = TRUE;
  7198. // turn off merc QUOTE flags
  7199. pVictim->fDyingComment = FALSE;
  7200. }
  7201. // update patient's entire panel (could have regained consciousness, etc.)
  7202. }
  7203. // if any healing points remain, apply that to any remaining bleeding (1/1)
  7204. // DON'T spend any APs/kit pts to cure bleeding until merc is no longer dying
  7205. //if ( ubPtsLeft && pVictim->bBleeding && !pVictim->dying)
  7206. if ( ubPtsLeft && pVictim->bBleeding )
  7207. {
  7208. // if we have enough points to bandage all remaining bleeding this turn
  7209. if (ubPtsLeft >= pVictim->bBleeding )
  7210. {
  7211. ubPtsLeft -= pVictim->bBleeding;
  7212. pVictim->bBleeding = 0;
  7213. }
  7214. else // bandage what we can
  7215. {
  7216. pVictim->bBleeding -= ubPtsLeft;
  7217. ubPtsLeft = 0;
  7218. }
  7219. // update patient's life bar only
  7220. }
  7221. // if wound has been dressed enough so that bleeding won't occur, turn off
  7222. // the "warned about bleeding" flag so merc tells us about the next bleeding
  7223. if ( pVictim->bBleeding <= MIN_BLEEDING_THRESHOLD )
  7224. {
  7225. pVictim->fWarnedAboutBleeding = FALSE;
  7226. }
  7227. // if there are any ptsLeft now, then we didn't actually get to use them
  7228. uiActual -= ubPtsLeft;
  7229. // usedAPs equals (actionPts) * (%of possible points actually used)
  7230. uiUsedAPs = ( uiActual * uiAvailAPs ) / uiPossible;
  7231. if (pSoldier->inv[ HANDPOS ].usItem == MEDICKIT) // using the GOOD medic stuff
  7232. {
  7233. uiUsedAPs = ( uiUsedAPs * 2) / 3; // reverse 50% bonus by taking 2/3rds
  7234. }
  7235. DeductPoints( pSoldier, (INT16)uiUsedAPs, (INT16)( ( uiUsedAPs * BP_PER_AP_LT_EFFORT) ) );
  7236. if ( PTR_OURTEAM )
  7237. {
  7238. // MEDICAL GAIN (actual / 2): Helped someone by giving first aid
  7239. StatChange(pSoldier, MEDICALAMT, (UINT16)(uiActual / 2), FALSE);
  7240. // DEXTERITY GAIN (actual / 6): Helped someone by giving first aid
  7241. StatChange(pSoldier, DEXTAMT, (UINT16)(uiActual / 6), FALSE);
  7242. }
  7243. return( uiMedcost );
  7244. }
  7245. void InternalReceivingSoldierCancelServices( SOLDIERTYPE *pSoldier, BOOLEAN fPlayEndAnim )
  7246. {
  7247. SOLDIERTYPE *pTSoldier;
  7248. INT32 cnt;
  7249. if ( pSoldier->ubServiceCount > 0 )
  7250. {
  7251. // Loop through guys who have us as servicing
  7252. for ( pTSoldier = Menptr, cnt = 0; cnt < MAX_NUM_SOLDIERS; pTSoldier++, cnt++ )
  7253. {
  7254. if ( pTSoldier->bActive )
  7255. {
  7256. if ( pTSoldier->ubServicePartner == pSoldier->ubID )
  7257. {
  7258. // END SERVICE!
  7259. pSoldier->ubServiceCount--;
  7260. pTSoldier->ubServicePartner = NOBODY;
  7261. if ( gTacticalStatus.fAutoBandageMode )
  7262. {
  7263. pSoldier->ubAutoBandagingMedic = NOBODY;
  7264. ActionDone( pTSoldier );
  7265. }
  7266. else
  7267. {
  7268. // don't use end aid animation in autobandage
  7269. if ( pTSoldier->bLife >= OKLIFE && pTSoldier->bBreath > 0 && fPlayEndAnim )
  7270. {
  7271. EVENT_InitNewSoldierAnim( pTSoldier, END_AID, 0 , FALSE );
  7272. }
  7273. }
  7274. }
  7275. }
  7276. }
  7277. }
  7278. }
  7279. void ReceivingSoldierCancelServices( SOLDIERTYPE *pSoldier )
  7280. {
  7281. InternalReceivingSoldierCancelServices( pSoldier, TRUE );
  7282. }
  7283. void InternalGivingSoldierCancelServices( SOLDIERTYPE *pSoldier, BOOLEAN fPlayEndAnim )
  7284. {
  7285. SOLDIERTYPE *pTSoldier;
  7286. // GET TARGET SOLDIER
  7287. if ( pSoldier->ubServicePartner != NOBODY )
  7288. {
  7289. pTSoldier = MercPtrs[ pSoldier->ubServicePartner ];
  7290. // END SERVICE!
  7291. pTSoldier->ubServiceCount--;
  7292. pSoldier->ubServicePartner = NOBODY;
  7293. if ( gTacticalStatus.fAutoBandageMode )
  7294. {
  7295. pTSoldier->ubAutoBandagingMedic = NOBODY;
  7296. ActionDone( pSoldier );
  7297. }
  7298. else
  7299. {
  7300. if ( pSoldier->bLife >= OKLIFE && pSoldier->bBreath > 0 && fPlayEndAnim )
  7301. {
  7302. // don't use end aid animation in autobandage
  7303. EVENT_InitNewSoldierAnim( pSoldier, END_AID, 0 , FALSE );
  7304. }
  7305. }
  7306. }
  7307. }
  7308. void GivingSoldierCancelServices( SOLDIERTYPE *pSoldier )
  7309. {
  7310. InternalGivingSoldierCancelServices( pSoldier, TRUE );
  7311. }
  7312. void HaultSoldierFromSighting( SOLDIERTYPE *pSoldier, BOOLEAN fFromSightingEnemy )
  7313. {
  7314. // SEND HUALT EVENT!
  7315. //EV_S_STOP_MERC SStopMerc;
  7316. //SStopMerc.sGridNo = pSoldier->sGridNo;
  7317. //SStopMerc.bDirection = pSoldier->bDirection;
  7318. //SStopMerc.usSoldierID = pSoldier->ubID;
  7319. //AddGameEvent( S_STOP_MERC, 0, &SStopMerc );
  7320. // If we are a 'specialmove... ignore...
  7321. if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_SPECIALMOVE ) )
  7322. {
  7323. return;
  7324. }
  7325. // OK, check if we were going to throw something, and give it back if so!
  7326. if ( pSoldier->pTempObject != NULL && fFromSightingEnemy )
  7327. {
  7328. // Place it back into inv....
  7329. AutoPlaceObject( pSoldier, pSoldier->pTempObject, FALSE );
  7330. MemFree( pSoldier->pTempObject );
  7331. pSoldier->pTempObject = NULL;
  7332. pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
  7333. pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION;
  7334. // Decrement attack counter...
  7335. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending throw because saw something") );
  7336. ReduceAttackBusyCount( pSoldier->ubID, FALSE );
  7337. // ATE: Goto stationary stance......
  7338. SoldierGotoStationaryStance( pSoldier );
  7339. DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 );
  7340. }
  7341. if ( !( gTacticalStatus.uiFlags & INCOMBAT ) )
  7342. {
  7343. EVENT_StopMerc( pSoldier, pSoldier->sGridNo, pSoldier->bDirection );
  7344. }
  7345. else
  7346. {
  7347. // Pause this guy from no APS
  7348. AdjustNoAPToFinishMove( pSoldier, TRUE );
  7349. pSoldier->ubReasonCantFinishMove = REASON_STOPPED_SIGHT;
  7350. // ATE; IF turning to shoot, stop!
  7351. // ATE: We want to do this only for enemies, not items....
  7352. if ( pSoldier->fTurningToShoot && fFromSightingEnemy )
  7353. {
  7354. pSoldier->fTurningToShoot = FALSE;
  7355. // Release attacker
  7356. // OK - this is hightly annoying , but due to the huge combinations of
  7357. // things that can happen - 1 of them is that sLastTarget will get unset
  7358. // after turn is done - so set flag here to tell it not to...
  7359. pSoldier->fDontUnsetLastTargetFromTurn = TRUE;
  7360. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending fire because saw something") );
  7361. ReduceAttackBusyCount( pSoldier->ubID, FALSE );
  7362. }
  7363. // OK, if we are stopped at our destination, cancel pending action...
  7364. if ( fFromSightingEnemy )
  7365. {
  7366. if ( pSoldier->ubPendingAction != NO_PENDING_ACTION && pSoldier->sGridNo == pSoldier->sFinalDestination )
  7367. {
  7368. pSoldier->ubPendingAction = NO_PENDING_ACTION;
  7369. }
  7370. // Stop pending animation....
  7371. pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
  7372. pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION;
  7373. }
  7374. if ( !pSoldier->fTurningToShoot )
  7375. {
  7376. pSoldier->fTurningFromPronePosition = FALSE;
  7377. }
  7378. }
  7379. // Unset UI!
  7380. if ( fFromSightingEnemy || ( pSoldier->pTempObject == NULL && !pSoldier->fTurningToShoot ) )
  7381. {
  7382. UnSetUIBusy( pSoldier->ubID );
  7383. }
  7384. pSoldier->bTurningFromUI = FALSE;
  7385. UnSetEngagedInConvFromPCAction( pSoldier );
  7386. }
  7387. // HUALT EVENT IS USED TO STOP A MERC - NETWORKING SHOULD CHECK / ADJUST TO GRIDNO?
  7388. void EVENT_StopMerc( SOLDIERTYPE *pSoldier, INT16 sGridNo, INT8 bDirection )
  7389. {
  7390. INT16 sX, sY;
  7391. // MOVE GUY TO GRIDNO--- SHOULD BE THE SAME UNLESS IN MULTIPLAYER
  7392. // Makesure center of tile
  7393. sX = CenterX( sGridNo );
  7394. sY = CenterY( sGridNo );
  7395. //Cancel pending events
  7396. if ( !pSoldier->fDelayedMovement )
  7397. {
  7398. pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
  7399. pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION;
  7400. pSoldier->ubPendingDirection = NO_PENDING_DIRECTION;
  7401. pSoldier->ubPendingAction = NO_PENDING_ACTION;
  7402. }
  7403. pSoldier->bEndDoorOpenCode = 0;
  7404. pSoldier->fTurningFromPronePosition = 0;
  7405. // Cancel path data!
  7406. pSoldier->usPathIndex = pSoldier->usPathDataSize = 0;
  7407. // Set ext tile waiting flag off!
  7408. pSoldier->fDelayedMovement = FALSE;
  7409. // Turn off reverse...
  7410. pSoldier->bReverse = FALSE;
  7411. EVENT_SetSoldierPosition( pSoldier, (FLOAT) sX, (FLOAT) sY );
  7412. pSoldier->sDestXPos = (INT16)pSoldier->dXPos;
  7413. pSoldier->sDestYPos = (INT16)pSoldier->dYPos;
  7414. EVENT_SetSoldierDirection(pSoldier, bDirection);
  7415. if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING )
  7416. {
  7417. SoldierGotoStationaryStance( pSoldier );
  7418. }
  7419. // ATE; IF turning to shoot, stop!
  7420. if ( pSoldier->fTurningToShoot )
  7421. {
  7422. pSoldier->fTurningToShoot = FALSE;
  7423. // Release attacker
  7424. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending fire because saw something") );
  7425. ReduceAttackBusyCount( pSoldier->ubID, FALSE );
  7426. }
  7427. // Turn off multi-move speed override....
  7428. if ( pSoldier->sGridNo == pSoldier->sFinalDestination )
  7429. {
  7430. pSoldier->fUseMoverrideMoveSpeed = FALSE;
  7431. }
  7432. // Unset UI!
  7433. UnSetUIBusy( pSoldier->ubID );
  7434. UnMarkMovementReserved( pSoldier );
  7435. }
  7436. void ReLoadSoldierAnimationDueToHandItemChange( SOLDIERTYPE *pSoldier, UINT16 usOldItem, UINT16 usNewItem )
  7437. {
  7438. // DON'T continue aiming!
  7439. // GOTO STANCE
  7440. // CHECK FOR AIMING ANIMATIONS
  7441. BOOLEAN fOldRifle = FALSE;
  7442. BOOLEAN fNewRifle = FALSE;
  7443. // Shutoff burst....
  7444. // ( we could be on, then change gun that does not have burst )
  7445. if ( Weapon[ usNewItem ].ubShotsPerBurst == 0 )
  7446. {
  7447. pSoldier->bDoBurst = FALSE;
  7448. pSoldier->bWeaponMode = WM_NORMAL;
  7449. }
  7450. if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY )
  7451. {
  7452. // Stop aiming!
  7453. SoldierGotoStationaryStance( pSoldier );
  7454. }
  7455. // Cancel services...
  7456. GivingSoldierCancelServices( pSoldier );
  7457. // Did we have a rifle and do we now not have one?
  7458. if ( usOldItem != NOTHING )
  7459. {
  7460. if ( Item[ usOldItem ].usItemClass == IC_GUN )
  7461. {
  7462. if ( (Item[ usOldItem ].fFlags & ITEM_TWO_HANDED) && usOldItem != ROCKET_LAUNCHER )
  7463. {
  7464. fOldRifle = TRUE;
  7465. }
  7466. }
  7467. }
  7468. if ( usNewItem != NOTHING )
  7469. {
  7470. if ( Item[ usNewItem ].usItemClass == IC_GUN )
  7471. {
  7472. if ( (Item[ usNewItem ].fFlags & ITEM_TWO_HANDED) && usNewItem != ROCKET_LAUNCHER )
  7473. {
  7474. fNewRifle = TRUE;
  7475. }
  7476. }
  7477. }
  7478. // Switch on stance!
  7479. switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
  7480. {
  7481. case ANIM_STAND:
  7482. if ( fOldRifle && !fNewRifle )
  7483. {
  7484. // Put it away!
  7485. EVENT_InitNewSoldierAnim( pSoldier, LOWER_RIFLE, 0 , FALSE );
  7486. }
  7487. else if ( !fOldRifle && fNewRifle )
  7488. {
  7489. // Bring it up!
  7490. EVENT_InitNewSoldierAnim( pSoldier, RAISE_RIFLE, 0 , FALSE );
  7491. }
  7492. else
  7493. {
  7494. SetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
  7495. }
  7496. break;
  7497. case ANIM_CROUCH:
  7498. case ANIM_PRONE:
  7499. SetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
  7500. break;
  7501. }
  7502. }
  7503. UINT16 *CreateEnemyGlow16BPPPalette( SGPPaletteEntry *pPalette, UINT32 rscale, UINT32 gscale, BOOLEAN fAdjustGreen )
  7504. {
  7505. UINT16 *p16BPPPalette, r16, g16, b16, usColor;
  7506. UINT32 cnt;
  7507. UINT32 rmod, gmod, bmod;
  7508. UINT8 r,g,b;
  7509. Assert( pPalette != NULL );
  7510. p16BPPPalette = MemAlloc( sizeof( UINT16 ) * 256 );
  7511. for ( cnt = 0; cnt < 256; cnt++ )
  7512. {
  7513. gmod = (pPalette[ cnt ].peGreen);
  7514. bmod = (pPalette[ cnt ].peBlue);
  7515. rmod = __max( rscale, (pPalette[ cnt ].peRed) );
  7516. if ( fAdjustGreen )
  7517. {
  7518. gmod = __max( gscale, (pPalette[ cnt ].peGreen) );
  7519. }
  7520. r = (UINT8)__min(rmod, 255);
  7521. g = (UINT8)__min(gmod, 255);
  7522. b = (UINT8)__min(bmod, 255);
  7523. if(gusRedShift < 0)
  7524. r16=((UINT16)r>>(-gusRedShift));
  7525. else
  7526. r16=((UINT16)r<<gusRedShift);
  7527. if(gusGreenShift < 0)
  7528. g16=((UINT16)g>>(-gusGreenShift));
  7529. else
  7530. g16=((UINT16)g<<gusGreenShift);
  7531. if(gusBlueShift < 0)
  7532. b16=((UINT16)b>>(-gusBlueShift));
  7533. else
  7534. b16=((UINT16)b<<gusBlueShift);
  7535. // Prevent creation of pure black color
  7536. usColor = (r16&gusRedMask)|(g16&gusGreenMask)|(b16&gusBlueMask);
  7537. if((usColor==0) && ((r+g+b)!=0))
  7538. usColor=0x0001;
  7539. p16BPPPalette[ cnt ] = usColor;
  7540. }
  7541. return( p16BPPPalette );
  7542. }
  7543. UINT16 *CreateEnemyGreyGlow16BPPPalette( SGPPaletteEntry *pPalette, UINT32 rscale, UINT32 gscale, BOOLEAN fAdjustGreen )
  7544. {
  7545. UINT16 *p16BPPPalette, r16, g16, b16, usColor;
  7546. UINT32 cnt, lumin;
  7547. UINT32 rmod, gmod, bmod;
  7548. UINT8 r,g,b;
  7549. Assert( pPalette != NULL );
  7550. p16BPPPalette = MemAlloc( sizeof( UINT16 ) * 256 );
  7551. for ( cnt = 0; cnt < 256; cnt++ )
  7552. {
  7553. lumin=(pPalette[ cnt ].peRed*299/1000)+ (pPalette[ cnt ].peGreen*587/1000)+(pPalette[ cnt ].peBlue*114/1000);
  7554. rmod=(100*lumin)/256;
  7555. gmod=(100*lumin)/256;
  7556. bmod=(100*lumin)/256;
  7557. rmod = __max( rscale, rmod );
  7558. if ( fAdjustGreen )
  7559. {
  7560. gmod = __max( gscale, gmod );
  7561. }
  7562. r = (UINT8)__min(rmod, 255);
  7563. g = (UINT8)__min(gmod, 255);
  7564. b = (UINT8)__min(bmod, 255);
  7565. if(gusRedShift < 0)
  7566. r16=((UINT16)r>>(-gusRedShift));
  7567. else
  7568. r16=((UINT16)r<<gusRedShift);
  7569. if(gusGreenShift < 0)
  7570. g16=((UINT16)g>>(-gusGreenShift));
  7571. else
  7572. g16=((UINT16)g<<gusGreenShift);
  7573. if(gusBlueShift < 0)
  7574. b16=((UINT16)b>>(-gusBlueShift));
  7575. else
  7576. b16=((UINT16)b<<gusBlueShift);
  7577. // Prevent creation of pure black color
  7578. usColor = (r16&gusRedMask)|(g16&gusGreenMask)|(b16&gusBlueMask);
  7579. if((usColor==0) && ((r+g+b)!=0))
  7580. usColor=0x0001;
  7581. p16BPPPalette[ cnt ] = usColor;
  7582. }
  7583. return( p16BPPPalette );
  7584. }
  7585. void ContinueMercMovement( SOLDIERTYPE *pSoldier )
  7586. {
  7587. INT16 sAPCost;
  7588. INT16 sGridNo;
  7589. sGridNo = pSoldier->sFinalDestination;
  7590. // Can we afford this?
  7591. if ( pSoldier->bGoodContPath )
  7592. {
  7593. sGridNo = pSoldier->sContPathLocation;
  7594. }
  7595. else
  7596. {
  7597. // ATE: OK, don't cancel count, so pending actions are still valid...
  7598. pSoldier->ubPendingActionAnimCount = 0;
  7599. }
  7600. // get a path to dest...
  7601. if ( FindBestPath( pSoldier, sGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, NO_COPYROUTE, 0 ) )
  7602. {
  7603. sAPCost = PtsToMoveDirection( pSoldier, (UINT8)guiPathingData[ 0 ] );
  7604. if ( EnoughPoints( pSoldier, sAPCost, 0 , (BOOLEAN)( pSoldier->bTeam == gbPlayerNum ) ) )
  7605. {
  7606. // Acknowledge
  7607. if ( pSoldier->bTeam == gbPlayerNum )
  7608. {
  7609. DoMercBattleSound( pSoldier, BATTLE_SOUND_OK1 );
  7610. // If we have a face, tell text in it to go away!
  7611. if ( pSoldier->iFaceIndex != -1 )
  7612. {
  7613. gFacesData[ pSoldier->iFaceIndex ].fDisplayTextOver = FACE_ERASE_TEXT_OVER;
  7614. }
  7615. }
  7616. AdjustNoAPToFinishMove( pSoldier, FALSE );
  7617. SetUIBusy( pSoldier->ubID );
  7618. // OK, try and get a path to out dest!
  7619. EVENT_InternalGetNewSoldierPath( pSoldier, sGridNo, pSoldier->usUIMovementMode, FALSE, TRUE );
  7620. }
  7621. }
  7622. }
  7623. BOOLEAN CheckForBreathCollapse( SOLDIERTYPE *pSoldier )
  7624. {
  7625. // Check if we are out of breath!
  7626. // Only check if > 70
  7627. if ( pSoldier->bBreathMax > 70 )
  7628. {
  7629. if ( pSoldier->bBreath < 20 && !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_LOW_BREATH ) &&
  7630. gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND )
  7631. {
  7632. // WARN!
  7633. TacticalCharacterDialogue( pSoldier, QUOTE_OUT_OF_BREATH );
  7634. // Set flag indicating we were warned!
  7635. pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_LOW_BREATH;
  7636. }
  7637. }
  7638. // Check for drowing.....
  7639. //if ( pSoldier->bBreath < 10 && !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_DROWNING ) && pSoldier->bOverTerrainType == DEEP_WATER )
  7640. //{
  7641. // WARN!
  7642. // TacticalCharacterDialogue( pSoldier, QUOTE_DROWNING );
  7643. // Set flag indicating we were warned!
  7644. // pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_DROWNING;
  7645. // WISDOM GAIN (25): Starting to drown
  7646. // StatChange( pSoldier, WISDOMAMT, 25, FALSE );
  7647. //}
  7648. if ( pSoldier->bBreath == 0 && !pSoldier->bCollapsed && !( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE | SOLDIER_ANIMAL | SOLDIER_MONSTER ) ) )
  7649. {
  7650. // Collapse!
  7651. // OK, Set a flag, because we may still be in the middle of an animation what is not interruptable...
  7652. pSoldier->bBreathCollapsed = TRUE;
  7653. return( TRUE );
  7654. }
  7655. return( FALSE );
  7656. }
  7657. BOOLEAN InternalIsValidStance( SOLDIERTYPE *pSoldier, INT8 bDirection, INT8 bNewStance )
  7658. {
  7659. UINT16 usOKToAddStructID=0;
  7660. STRUCTURE_FILE_REF *pStructureFileRef;
  7661. UINT16 usAnimSurface=0;
  7662. UINT16 usAnimState;
  7663. // Check, if dest is prone, we can actually do this!
  7664. // If we are a vehicle, we can only 'stand'
  7665. if ( ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) && bNewStance != ANIM_STAND )
  7666. {
  7667. return( FALSE );
  7668. }
  7669. // Check if we are in water?
  7670. if ( MercInWater( pSoldier ) )
  7671. {
  7672. if ( bNewStance == ANIM_PRONE || bNewStance == ANIM_CROUCH )
  7673. {
  7674. return( FALSE );
  7675. }
  7676. }
  7677. if ( pSoldier->ubBodyType == ROBOTNOWEAPON && bNewStance != ANIM_STAND )
  7678. {
  7679. return( FALSE );
  7680. }
  7681. // Check if we are in water?
  7682. if ( AM_AN_EPC( pSoldier ) )
  7683. {
  7684. if ( bNewStance == ANIM_PRONE )
  7685. {
  7686. return( FALSE );
  7687. }
  7688. else
  7689. {
  7690. return( TRUE );
  7691. }
  7692. }
  7693. if ( pSoldier->bCollapsed )
  7694. {
  7695. if ( bNewStance == ANIM_STAND || bNewStance == ANIM_CROUCH )
  7696. {
  7697. return( FALSE );
  7698. }
  7699. }
  7700. // Check if we can do this....
  7701. if ( pSoldier->pLevelNode && pSoldier->pLevelNode->pStructureData != NULL )
  7702. {
  7703. usOKToAddStructID = pSoldier->pLevelNode->pStructureData->usStructureID;
  7704. }
  7705. else
  7706. {
  7707. usOKToAddStructID = INVALID_STRUCTURE_ID;
  7708. }
  7709. switch( bNewStance )
  7710. {
  7711. case ANIM_STAND:
  7712. usAnimState = STANDING;
  7713. break;
  7714. case ANIM_CROUCH:
  7715. usAnimState = CROUCHING;
  7716. break;
  7717. case ANIM_PRONE:
  7718. usAnimState = PRONE;
  7719. break;
  7720. default:
  7721. // Something gone funny here....
  7722. usAnimState = pSoldier->usAnimState;
  7723. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"Wrong desired stance given: %d, %d.", bNewStance, pSoldier->usAnimState );
  7724. }
  7725. usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, usAnimState );
  7726. // Get structure ref........
  7727. pStructureFileRef = GetAnimationStructureRef( pSoldier->ubID, usAnimSurface, usAnimState );
  7728. if ( pStructureFileRef != NULL )
  7729. {
  7730. // Can we add structure data for this stance...?
  7731. if ( !OkayToAddStructureToWorld( pSoldier->sGridNo, pSoldier->bLevel, &(pStructureFileRef->pDBStructureRef[gOneCDirection[ bDirection ]]), usOKToAddStructID ) )
  7732. {
  7733. return( FALSE );
  7734. }
  7735. }
  7736. return( TRUE );
  7737. }
  7738. BOOLEAN IsValidStance( SOLDIERTYPE *pSoldier, INT8 bNewStance )
  7739. {
  7740. return( InternalIsValidStance( pSoldier, pSoldier->bDirection, bNewStance ) );
  7741. }
  7742. BOOLEAN IsValidMovementMode( SOLDIERTYPE *pSoldier, INT16 usMovementMode )
  7743. {
  7744. // Check, if dest is prone, we can actually do this!
  7745. // Check if we are in water?
  7746. if ( MercInWater( pSoldier ) )
  7747. {
  7748. if ( usMovementMode == RUNNING || usMovementMode == SWATTING || usMovementMode == CRAWLING )
  7749. {
  7750. return( FALSE );
  7751. }
  7752. }
  7753. return( TRUE );
  7754. }
  7755. void SelectMoveAnimationFromStance( SOLDIERTYPE *pSoldier )
  7756. {
  7757. // Determine which animation to do...depending on stance and gun in hand...
  7758. switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
  7759. {
  7760. case ANIM_STAND:
  7761. EVENT_InitNewSoldierAnim( pSoldier, WALKING, 0 , FALSE );
  7762. break;
  7763. case ANIM_PRONE:
  7764. EVENT_InitNewSoldierAnim( pSoldier, CRAWLING, 0 , FALSE );
  7765. break;
  7766. case ANIM_CROUCH:
  7767. EVENT_InitNewSoldierAnim( pSoldier, SWATTING, 0 , FALSE );
  7768. break;
  7769. }
  7770. }
  7771. void GetActualSoldierAnimDims( SOLDIERTYPE *pSoldier, INT16 *psHeight, INT16 *psWidth )
  7772. {
  7773. UINT16 usAnimSurface;
  7774. ETRLEObject *pTrav;
  7775. usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
  7776. if ( usAnimSurface == INVALID_ANIMATION_SURFACE )
  7777. {
  7778. *psHeight = (INT16)5;
  7779. *psWidth = (INT16)5;
  7780. return;
  7781. }
  7782. if ( gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject == NULL )
  7783. {
  7784. *psHeight = (INT16)5;
  7785. *psWidth = (INT16)5;
  7786. return;
  7787. }
  7788. // OK, noodle here on what we should do... If we take each frame, it will be different slightly
  7789. // depending on the frame and the value returned here will vary thusly. However, for the
  7790. // uses of this function, we should be able to use just the first frame...
  7791. if ( pSoldier->usAniFrame >= gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->usNumberOfObjects )
  7792. {
  7793. int i = 0;
  7794. }
  7795. pTrav = &(gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->pETRLEObject[ pSoldier->usAniFrame ] );
  7796. *psHeight = (INT16)pTrav->usHeight;
  7797. *psWidth = (INT16)pTrav->usWidth;
  7798. }
  7799. void GetActualSoldierAnimOffsets( SOLDIERTYPE *pSoldier, INT16 *sOffsetX, INT16 *sOffsetY )
  7800. {
  7801. UINT16 usAnimSurface;
  7802. ETRLEObject *pTrav;
  7803. usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
  7804. if ( usAnimSurface == INVALID_ANIMATION_SURFACE )
  7805. {
  7806. *sOffsetX = (INT16)0;
  7807. *sOffsetY = (INT16)0;
  7808. return;
  7809. }
  7810. if ( gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject == NULL )
  7811. {
  7812. *sOffsetX = (INT16)0;
  7813. *sOffsetY = (INT16)0;
  7814. return;
  7815. }
  7816. pTrav = &(gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->pETRLEObject[ pSoldier->usAniFrame ] );
  7817. *sOffsetX = (INT16)pTrav->sOffsetX;
  7818. *sOffsetY = (INT16)pTrav->sOffsetY;
  7819. }
  7820. void SetSoldierLocatorOffsets( SOLDIERTYPE *pSoldier )
  7821. {
  7822. INT16 sHeight, sWidth;
  7823. INT16 sOffsetX, sOffsetY;
  7824. // OK, from our animation, get height, width
  7825. GetActualSoldierAnimDims( pSoldier, &sHeight, &sWidth );
  7826. GetActualSoldierAnimOffsets( pSoldier, &sOffsetX, &sOffsetY );
  7827. // OK, here, use the difference between center of animation ( sWidth/2 ) and our offset!
  7828. //pSoldier->sLocatorOffX = ( abs( sOffsetX ) ) - ( sWidth / 2 );
  7829. pSoldier->sBoundingBoxWidth = sWidth;
  7830. pSoldier->sBoundingBoxHeight = sHeight;
  7831. pSoldier->sBoundingBoxOffsetX = sOffsetX;
  7832. pSoldier->sBoundingBoxOffsetY = sOffsetY;
  7833. }
  7834. BOOLEAN SoldierCarriesTwoHandedWeapon( SOLDIERTYPE *pSoldier )
  7835. {
  7836. UINT16 usItem;
  7837. usItem = pSoldier->inv[ HANDPOS ].usItem;
  7838. if ( usItem != NOTHING && (Item[ usItem ].fFlags & ITEM_TWO_HANDED) )
  7839. {
  7840. return( TRUE );
  7841. }
  7842. return( FALSE );
  7843. }
  7844. INT32 CheckBleeding( SOLDIERTYPE *pSoldier )
  7845. {
  7846. INT8 bBandaged; //,savedOurTurn;
  7847. INT32 iBlood = NOBLOOD;
  7848. if ( pSoldier->bLife != 0 )
  7849. {
  7850. // if merc is hurt beyond the minimum required to bleed, or he's dying
  7851. if ( ( pSoldier->bBleeding > MIN_BLEEDING_THRESHOLD) || pSoldier->bLife < OKLIFE )
  7852. {
  7853. // if he's NOT in the process of being bandaged or DOCTORed
  7854. if ( ( pSoldier->ubServiceCount == 0 ) && ( AnyDoctorWhoCanHealThisPatient( pSoldier, HEALABLE_EVER ) == NULL ) )
  7855. {
  7856. // may drop blood whether or not any bleeding takes place this turn
  7857. if ( pSoldier->bTilesMoved < 1 )
  7858. {
  7859. iBlood = ( ( pSoldier->bBleeding - MIN_BLEEDING_THRESHOLD ) / BLOODDIVISOR ); // + pSoldier->dying;
  7860. if ( iBlood > MAXBLOODQUANTITY )
  7861. {
  7862. iBlood = MAXBLOODQUANTITY;
  7863. }
  7864. }
  7865. else
  7866. {
  7867. iBlood = NOBLOOD;
  7868. }
  7869. // Are we in a different mode?
  7870. if ( !(gTacticalStatus.uiFlags & TURNBASED ) || !(gTacticalStatus.uiFlags & INCOMBAT ) )
  7871. {
  7872. pSoldier->dNextBleed -= (FLOAT)RT_NEXT_BLEED_MODIFIER;
  7873. }
  7874. else
  7875. {
  7876. // Do a single step descrease
  7877. pSoldier->dNextBleed--;
  7878. }
  7879. // if it's time to lose some blood
  7880. if ( pSoldier->dNextBleed <= 0)
  7881. {
  7882. // first, calculate if soldier is bandaged
  7883. bBandaged = pSoldier->bLifeMax - pSoldier->bBleeding - pSoldier->bLife;
  7884. // as long as he's bandaged and not "dying"
  7885. if ( bBandaged && pSoldier->bLife >= OKLIFE )
  7886. {
  7887. // just bleeding through existing bandages
  7888. pSoldier->bBleeding++;
  7889. SoldierBleed( pSoldier, TRUE );
  7890. }
  7891. else // soldier is either not bandaged at all or is dying
  7892. {
  7893. if ( pSoldier->bLife < OKLIFE ) // if he's dying
  7894. {
  7895. // if he's conscious, and he hasn't already, say his "dying quote"
  7896. if ( ( pSoldier->bLife >= CONSCIOUSNESS ) && !pSoldier->fDyingComment )
  7897. {
  7898. TacticalCharacterDialogue( pSoldier, QUOTE_SERIOUSLY_WOUNDED );
  7899. pSoldier->fDyingComment = TRUE;
  7900. }
  7901. // can't permit lifemax to ever bleed beneath OKLIFE, or that
  7902. // soldier might as well be dead!
  7903. if (pSoldier->bLifeMax >= OKLIFE)
  7904. {
  7905. // bleeding while "dying" costs a PERMANENT point of life each time!
  7906. pSoldier->bLifeMax--;
  7907. pSoldier->bBleeding--;
  7908. }
  7909. }
  7910. }
  7911. // either way, a point of life (health) is lost because of bleeding
  7912. // This will also update the life bar
  7913. SoldierBleed( pSoldier, FALSE );
  7914. // if he's not dying (which includes him saying the dying quote just
  7915. // now), and he hasn't warned us that he's bleeding yet, he does so
  7916. // Also, not if they are being bandaged....
  7917. if ( ( pSoldier->bLife >= OKLIFE ) && !pSoldier->fDyingComment && !pSoldier->fWarnedAboutBleeding && !gTacticalStatus.fAutoBandageMode && pSoldier->ubServiceCount == 0 )
  7918. {
  7919. TacticalCharacterDialogue( pSoldier, QUOTE_STARTING_TO_BLEED );
  7920. // "starting to bleed" quote
  7921. pSoldier->fWarnedAboutBleeding = TRUE;
  7922. }
  7923. pSoldier->dNextBleed = CalcSoldierNextBleed( pSoldier );
  7924. }
  7925. }
  7926. }
  7927. }
  7928. return( iBlood );
  7929. }
  7930. void SoldierBleed( SOLDIERTYPE *pSoldier, BOOLEAN fBandagedBleed )
  7931. {
  7932. INT8 bOldLife;
  7933. // OK, here make some stuff happen for bleeding
  7934. // A banaged bleed does not show damage taken , just through existing bandages
  7935. // ATE: Do this ONLY if buddy is in sector.....
  7936. if ( ( pSoldier->bInSector && guiCurrentScreen == GAME_SCREEN ) || guiCurrentScreen != GAME_SCREEN )
  7937. {
  7938. pSoldier->fFlashPortrait = TRUE;
  7939. pSoldier->bFlashPortraitFrame = FLASH_PORTRAIT_STARTSHADE;
  7940. RESETTIMECOUNTER( pSoldier->PortraitFlashCounter, FLASH_PORTRAIT_DELAY );
  7941. // If we are in mapscreen, set this person as selected
  7942. if ( guiCurrentScreen == MAP_SCREEN )
  7943. {
  7944. SetInfoChar( pSoldier->ubID );
  7945. }
  7946. }
  7947. bOldLife = pSoldier->bLife;
  7948. // If we are already dead, don't show damage!
  7949. if ( !fBandagedBleed )
  7950. {
  7951. SoldierTakeDamage( pSoldier, ANIM_CROUCH, 1, 100, TAKE_DAMAGE_BLOODLOSS, NOBODY, NOWHERE, 0, TRUE );
  7952. }
  7953. }
  7954. void SoldierCollapse( SOLDIERTYPE *pSoldier )
  7955. {
  7956. BOOLEAN fMerc = FALSE;
  7957. if ( pSoldier->ubBodyType <= REGFEMALE )
  7958. {
  7959. fMerc = TRUE;
  7960. }
  7961. // If we are an animal, etc, don't do anything....
  7962. switch( pSoldier->ubBodyType )
  7963. {
  7964. case ADULTFEMALEMONSTER:
  7965. case AM_MONSTER:
  7966. case YAF_MONSTER:
  7967. case YAM_MONSTER:
  7968. case LARVAE_MONSTER:
  7969. case INFANT_MONSTER:
  7970. case QUEENMONSTER:
  7971. // Give breath back....
  7972. DeductPoints( pSoldier,0, (INT16)-5000 );
  7973. return;
  7974. break;
  7975. }
  7976. pSoldier->bCollapsed = TRUE;
  7977. ReceivingSoldierCancelServices( pSoldier );
  7978. // CC has requested - handle sight here...
  7979. HandleSight( pSoldier, SIGHT_LOOK );
  7980. // Check height
  7981. switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
  7982. {
  7983. case ANIM_STAND:
  7984. if ( pSoldier->bOverTerrainType == DEEP_WATER )
  7985. {
  7986. EVENT_InitNewSoldierAnim( pSoldier, DEEP_WATER_DIE, 0, FALSE );
  7987. }
  7988. else if ( pSoldier->bOverTerrainType == LOW_WATER )
  7989. {
  7990. EVENT_InitNewSoldierAnim( pSoldier, WATER_DIE, 0, FALSE );
  7991. }
  7992. else
  7993. {
  7994. BeginTyingToFall( pSoldier );
  7995. EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0, FALSE );
  7996. }
  7997. break;
  7998. case ANIM_CROUCH:
  7999. // Crouched or prone, only for mercs!
  8000. BeginTyingToFall( pSoldier );
  8001. if ( fMerc )
  8002. {
  8003. EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_CROUCH, 0 , FALSE);
  8004. }
  8005. else
  8006. {
  8007. // For civs... use fall from stand...
  8008. EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0 , FALSE);
  8009. }
  8010. break;
  8011. case ANIM_PRONE:
  8012. switch( pSoldier->usAnimState )
  8013. {
  8014. case FALLFORWARD_FROMHIT_STAND:
  8015. case ENDFALLFORWARD_FROMHIT_CROUCH:
  8016. ChangeSoldierState( pSoldier, STAND_FALLFORWARD_STOP, 0, FALSE );
  8017. break;
  8018. case FALLBACK_HIT_STAND:
  8019. ChangeSoldierState( pSoldier, FALLBACKHIT_STOP, 0, FALSE );
  8020. break;
  8021. default:
  8022. EVENT_InitNewSoldierAnim( pSoldier, PRONE_LAY_FROMHIT, 0 , FALSE );
  8023. break;
  8024. }
  8025. break;
  8026. }
  8027. if (pSoldier->uiStatusFlags & SOLDIER_ENEMY)
  8028. {
  8029. if ( !(gTacticalStatus.bPanicTriggerIsAlarm) && (gTacticalStatus.ubTheChosenOne == pSoldier->ubID) )
  8030. {
  8031. // replace this guy as the chosen one!
  8032. gTacticalStatus.ubTheChosenOne = NOBODY;
  8033. MakeClosestEnemyChosenOne();
  8034. }
  8035. if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) && (pSoldier->uiStatusFlags & SOLDIER_UNDERAICONTROL))
  8036. {
  8037. #ifdef TESTAICONTROL
  8038. DebugAI( String("Ending turn for %d because of error from HandleItem", pSoldier->ubID ) );
  8039. #endif
  8040. EndAIGuysTurn( pSoldier );
  8041. }
  8042. }
  8043. // DON'T DE-SELECT GUY.....
  8044. //else
  8045. //{
  8046. // Check if this is our selected guy...
  8047. // if ( pSoldier->ubID == gusSelectedSoldier )
  8048. // {
  8049. // SelectNextAvailSoldier( pSoldier );
  8050. // }
  8051. //}
  8052. }
  8053. FLOAT CalcSoldierNextBleed( SOLDIERTYPE *pSoldier )
  8054. {
  8055. INT8 bBandaged;
  8056. // calculate how many turns before he bleeds again
  8057. // bleeding faster the lower life gets, and if merc is running around
  8058. //pSoldier->nextbleed = 2 + (pSoldier->life / (10 + pSoldier->tilesMoved)); // min = 2
  8059. // if bandaged, give 1/2 of the bandaged life points back into equation
  8060. bBandaged = pSoldier->bLifeMax - pSoldier->bLife - pSoldier->bBleeding;
  8061. return( (FLOAT)1 + (FLOAT)( (pSoldier->bLife + bBandaged / 2) / (10 + pSoldier->bTilesMoved) ) ); // min = 1
  8062. }
  8063. FLOAT CalcSoldierNextUnmovingBleed( SOLDIERTYPE *pSoldier )
  8064. {
  8065. INT8 bBandaged;
  8066. // calculate bleeding rate without the penalty for tiles moved
  8067. // if bandaged, give 1/2 of the bandaged life points back into equation
  8068. bBandaged = pSoldier->bLifeMax - pSoldier->bLife - pSoldier->bBleeding;
  8069. return( (FLOAT)1 + (FLOAT)( (pSoldier->bLife + bBandaged / 2) / 10 ) ); // min = 1
  8070. }
  8071. void HandlePlacingRoofMarker( SOLDIERTYPE *pSoldier, INT16 sGridNo, BOOLEAN fSet, BOOLEAN fForce )
  8072. {
  8073. LEVELNODE *pRoofNode;
  8074. LEVELNODE *pNode;
  8075. if ( pSoldier->bVisible == -1 && fSet )
  8076. {
  8077. return;
  8078. }
  8079. if ( pSoldier->bTeam != gbPlayerNum )
  8080. {
  8081. //return;
  8082. }
  8083. // If we are on the roof, add roof UI peice!
  8084. if ( pSoldier->bLevel == SECOND_LEVEL )
  8085. {
  8086. // Get roof node
  8087. pRoofNode = gpWorldLevelData[ sGridNo ].pRoofHead;
  8088. // Return if we are still climbing roof....
  8089. if ( pSoldier->usAnimState == CLIMBUPROOF && !fForce )
  8090. {
  8091. return;
  8092. }
  8093. if ( pRoofNode != NULL )
  8094. {
  8095. if ( fSet )
  8096. {
  8097. if ( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REVEALED )
  8098. {
  8099. // Set some flags on this poor thing
  8100. //pRoofNode->uiFlags |= ( LEVELNODE_USEBESTTRANSTYPE | LEVELNODE_REVEAL | LEVELNODE_DYNAMIC );
  8101. //pRoofNode->uiFlags |= ( LEVELNODE_DYNAMIC );
  8102. //pRoofNode->uiFlags &= ( ~LEVELNODE_HIDDEN );
  8103. //ResetSpecificLayerOptimizing( TILES_DYNAMIC_ROOF );
  8104. }
  8105. }
  8106. else
  8107. {
  8108. if ( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REVEALED )
  8109. {
  8110. // Remove some flags on this poor thing
  8111. //pRoofNode->uiFlags &= ~( LEVELNODE_USEBESTTRANSTYPE | LEVELNODE_REVEAL | LEVELNODE_DYNAMIC );
  8112. //pRoofNode->uiFlags |= LEVELNODE_HIDDEN;
  8113. }
  8114. }
  8115. if ( fSet )
  8116. {
  8117. // If it does not exist already....
  8118. if ( !IndexExistsInRoofLayer( sGridNo, FIRSTPOINTERS11 ) )
  8119. {
  8120. pNode = AddRoofToTail( sGridNo, FIRSTPOINTERS11 );
  8121. pNode->ubShadeLevel =DEFAULT_SHADE_LEVEL;
  8122. pNode->ubNaturalShadeLevel=DEFAULT_SHADE_LEVEL;
  8123. }
  8124. }
  8125. else
  8126. {
  8127. RemoveRoof( sGridNo, FIRSTPOINTERS11 );
  8128. }
  8129. }
  8130. }
  8131. }
  8132. void PositionSoldierLight( SOLDIERTYPE *pSoldier )
  8133. {
  8134. // DO ONLY IF WE'RE AT A GOOD LEVEL
  8135. if ( ubAmbientLightLevel < MIN_AMB_LEVEL_FOR_MERC_LIGHTS )
  8136. {
  8137. return;
  8138. }
  8139. if ( !pSoldier->bInSector )
  8140. {
  8141. return;
  8142. }
  8143. if ( pSoldier->bTeam != gbPlayerNum )
  8144. {
  8145. return;
  8146. }
  8147. if ( pSoldier->bLife < OKLIFE )
  8148. {
  8149. return;
  8150. }
  8151. //if the player DOESNT want the merc to cast light
  8152. if( !gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] )
  8153. {
  8154. return;
  8155. }
  8156. if ( pSoldier->iLight == -1 )
  8157. {
  8158. CreateSoldierLight( pSoldier );
  8159. }
  8160. //if ( pSoldier->ubID == gusSelectedSoldier )
  8161. {
  8162. LightSpritePower(pSoldier->iLight, TRUE);
  8163. LightSpriteFake(pSoldier->iLight);
  8164. LightSpritePosition( pSoldier->iLight, (INT16)(pSoldier->sX/CELL_X_SIZE), (INT16)(pSoldier->sY/CELL_Y_SIZE));
  8165. }
  8166. }
  8167. void SetCheckSoldierLightFlag( SOLDIERTYPE *pSoldier )
  8168. {
  8169. PositionSoldierLight( pSoldier );
  8170. //pSoldier->uiStatusFlags |= SOLDIER_RECHECKLIGHT;
  8171. }
  8172. void PickPickupAnimation( SOLDIERTYPE *pSoldier, INT32 iItemIndex, INT16 sGridNo, INT8 bZLevel )
  8173. {
  8174. INT8 bDirection;
  8175. STRUCTURE *pStructure;
  8176. BOOLEAN fDoNormalPickup = TRUE;
  8177. // OK, Given the gridno, determine if it's the same one or different....
  8178. if ( sGridNo != pSoldier->sGridNo )
  8179. {
  8180. // Get direction to face....
  8181. bDirection = (INT8)GetDirectionFromGridNo( sGridNo, pSoldier );
  8182. pSoldier->ubPendingDirection = bDirection;
  8183. // Change to pickup animation
  8184. EVENT_InitNewSoldierAnim( pSoldier, ADJACENT_GET_ITEM, 0 , FALSE );
  8185. if (!(pSoldier->uiStatusFlags & SOLDIER_PC))
  8186. {
  8187. // set "pending action" value for AI so it will wait
  8188. pSoldier->bAction = AI_ACTION_PENDING_ACTION;
  8189. }
  8190. }
  8191. else
  8192. {
  8193. // If in water....
  8194. if ( MercInWater( pSoldier ) )
  8195. {
  8196. UnSetUIBusy( pSoldier->ubID );
  8197. HandleSoldierPickupItem( pSoldier, iItemIndex, sGridNo, bZLevel );
  8198. SoldierGotoStationaryStance( pSoldier );
  8199. if (!(pSoldier->uiStatusFlags & SOLDIER_PC))
  8200. {
  8201. // reset action value for AI because we're done!
  8202. ActionDone( pSoldier );
  8203. }
  8204. }
  8205. else
  8206. {
  8207. // Don't show animation of getting item, if we are not standing
  8208. switch ( gAnimControl[ pSoldier->usAnimState ].ubHeight )
  8209. {
  8210. case ANIM_STAND:
  8211. // OK, if we are looking at z-level >0, AND
  8212. // we have a strucxture with items in it
  8213. // look for orientation and use angle accordingly....
  8214. if ( bZLevel > 0 )
  8215. {
  8216. //#if 0
  8217. // Get direction to face....
  8218. if ( ( pStructure = FindStructure( (INT16)sGridNo, ( STRUCTURE_HASITEMONTOP | STRUCTURE_OPENABLE ) ) ) != NULL )
  8219. {
  8220. fDoNormalPickup = FALSE;
  8221. // OK, look at orientation
  8222. switch( pStructure->ubWallOrientation )
  8223. {
  8224. case OUTSIDE_TOP_LEFT:
  8225. case INSIDE_TOP_LEFT:
  8226. bDirection = (INT8)NORTH;
  8227. break;
  8228. case OUTSIDE_TOP_RIGHT:
  8229. case INSIDE_TOP_RIGHT:
  8230. bDirection = (INT8)WEST;
  8231. break;
  8232. default:
  8233. bDirection = pSoldier->bDirection;
  8234. break;
  8235. }
  8236. //pSoldier->ubPendingDirection = bDirection;
  8237. EVENT_SetSoldierDesiredDirection( pSoldier, bDirection );
  8238. EVENT_SetSoldierDirection( pSoldier, bDirection );
  8239. // Change to pickup animation
  8240. EVENT_InitNewSoldierAnim( pSoldier, ADJACENT_GET_ITEM, 0 , FALSE );
  8241. }
  8242. //#endif
  8243. }
  8244. if ( fDoNormalPickup )
  8245. {
  8246. EVENT_InitNewSoldierAnim( pSoldier, PICKUP_ITEM, 0 , FALSE );
  8247. }
  8248. if (!(pSoldier->uiStatusFlags & SOLDIER_PC))
  8249. {
  8250. // set "pending action" value for AI so it will wait
  8251. pSoldier->bAction = AI_ACTION_PENDING_ACTION;
  8252. }
  8253. break;
  8254. case ANIM_CROUCH:
  8255. case ANIM_PRONE:
  8256. UnSetUIBusy( pSoldier->ubID );
  8257. HandleSoldierPickupItem( pSoldier, iItemIndex, sGridNo, bZLevel );
  8258. SoldierGotoStationaryStance( pSoldier );
  8259. if (!(pSoldier->uiStatusFlags & SOLDIER_PC))
  8260. {
  8261. // reset action value for AI because we're done!
  8262. ActionDone( pSoldier );
  8263. }
  8264. break;
  8265. }
  8266. }
  8267. }
  8268. }
  8269. void PickDropItemAnimation( SOLDIERTYPE *pSoldier )
  8270. {
  8271. // Don't show animation of getting item, if we are not standing
  8272. switch ( gAnimControl[ pSoldier->usAnimState ].ubHeight )
  8273. {
  8274. case ANIM_STAND:
  8275. EVENT_InitNewSoldierAnim( pSoldier, DROP_ITEM, 0 , FALSE );
  8276. break;
  8277. case ANIM_CROUCH:
  8278. case ANIM_PRONE:
  8279. SoldierHandleDropItem( pSoldier );
  8280. SoldierGotoStationaryStance( pSoldier );
  8281. break;
  8282. }
  8283. }
  8284. void EVENT_SoldierBeginCutFence( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
  8285. {
  8286. // Make sure we have a structure here....
  8287. if ( IsCuttableWireFenceAtGridNo( sGridNo ) )
  8288. {
  8289. // CHANGE DIRECTION AND GOTO ANIMATION NOW
  8290. EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
  8291. EVENT_SetSoldierDirection( pSoldier, ubDirection );
  8292. //BOOLEAN CutWireFence( INT16 sGridNo )
  8293. // SET TARGET GRIDNO
  8294. pSoldier->sTargetGridNo = sGridNo;
  8295. // CHANGE TO ANIMATION
  8296. EVENT_InitNewSoldierAnim( pSoldier, CUTTING_FENCE, 0 , FALSE );
  8297. }
  8298. }
  8299. void EVENT_SoldierBeginRepair( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
  8300. {
  8301. INT8 bRepairItem;
  8302. UINT8 ubID;
  8303. // Make sure we have a structure here....
  8304. bRepairItem = IsRepairableStructAtGridNo( sGridNo, &ubID );
  8305. if ( bRepairItem )
  8306. {
  8307. // CHANGE DIRECTION AND GOTO ANIMATION NOW
  8308. EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
  8309. EVENT_SetSoldierDirection( pSoldier, ubDirection );
  8310. //BOOLEAN CutWireFence( INT16 sGridNo )
  8311. // SET TARGET GRIDNO
  8312. //pSoldier->sTargetGridNo = sGridNo;
  8313. // CHANGE TO ANIMATION
  8314. EVENT_InitNewSoldierAnim( pSoldier, GOTO_REPAIRMAN, 0 , FALSE );
  8315. // SET BUDDY'S ASSIGNMENT TO REPAIR...
  8316. // Are we a SAM site? ( 3 == SAM )
  8317. if ( bRepairItem == 3 )
  8318. {
  8319. SetSoldierAssignment( pSoldier, REPAIR, TRUE, FALSE, -1 );
  8320. }
  8321. else if ( bRepairItem == 2 ) // ( 2 == VEHICLE )
  8322. {
  8323. SetSoldierAssignment( pSoldier, REPAIR, FALSE, FALSE, ubID );
  8324. }
  8325. }
  8326. }
  8327. void EVENT_SoldierBeginRefuel( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
  8328. {
  8329. INT8 bRefuelItem;
  8330. UINT8 ubID;
  8331. // Make sure we have a structure here....
  8332. bRefuelItem = IsRefuelableStructAtGridNo( sGridNo, &ubID );
  8333. if ( bRefuelItem )
  8334. {
  8335. // CHANGE DIRECTION AND GOTO ANIMATION NOW
  8336. EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
  8337. EVENT_SetSoldierDirection( pSoldier, ubDirection );
  8338. //BOOLEAN CutWireFence( INT16 sGridNo )
  8339. // SET TARGET GRIDNO
  8340. //pSoldier->sTargetGridNo = sGridNo;
  8341. // CHANGE TO ANIMATION
  8342. EVENT_InitNewSoldierAnim( pSoldier, REFUEL_VEHICLE, 0 , FALSE );
  8343. // SET BUDDY'S ASSIGNMENT TO REPAIR...
  8344. }
  8345. }
  8346. void EVENT_SoldierBeginTakeBlood( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
  8347. {
  8348. ROTTING_CORPSE *pCorpse;
  8349. // See if these is a corpse here....
  8350. pCorpse = GetCorpseAtGridNo( sGridNo , pSoldier->bLevel );
  8351. if ( pCorpse != NULL )
  8352. {
  8353. pSoldier->uiPendingActionData4 = pCorpse->iID;
  8354. // CHANGE DIRECTION AND GOTO ANIMATION NOW
  8355. EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
  8356. EVENT_SetSoldierDirection( pSoldier, ubDirection );
  8357. EVENT_InitNewSoldierAnim( pSoldier, TAKE_BLOOD_FROM_CORPSE, 0 , FALSE );
  8358. }
  8359. else
  8360. {
  8361. // Say NOTHING quote...
  8362. DoMercBattleSound( pSoldier, BATTLE_SOUND_NOTHING );
  8363. }
  8364. }
  8365. void EVENT_SoldierBeginAttachCan( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
  8366. {
  8367. STRUCTURE * pStructure;
  8368. DOOR_STATUS * pDoorStatus;
  8369. // OK, find door, attach to door, do animation...., remove item....
  8370. // First make sure we still have item in hand....
  8371. if ( pSoldier->inv[ HANDPOS ].usItem != STRING_TIED_TO_TIN_CAN )
  8372. {
  8373. return;
  8374. }
  8375. pStructure = FindStructure( sGridNo, STRUCTURE_ANYDOOR );
  8376. if ( pStructure == NULL )
  8377. {
  8378. return;
  8379. }
  8380. // Modify door status to make sure one is created for this door
  8381. // Use the current door state for this
  8382. if ( !(pStructure->fFlags & STRUCTURE_OPEN) )
  8383. {
  8384. ModifyDoorStatus( sGridNo, FALSE, FALSE );
  8385. }
  8386. else
  8387. {
  8388. ModifyDoorStatus( sGridNo, TRUE, TRUE );
  8389. }
  8390. // Now get door status...
  8391. pDoorStatus = GetDoorStatus( sGridNo );
  8392. if ( pDoorStatus == NULL )
  8393. {
  8394. // SOmething wrong here...
  8395. return;
  8396. }
  8397. // OK set flag!
  8398. pDoorStatus->ubFlags |= DOOR_HAS_TIN_CAN;
  8399. // Do animation
  8400. EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
  8401. EVENT_SetSoldierDirection( pSoldier, ubDirection );
  8402. EVENT_InitNewSoldierAnim( pSoldier, ATTACH_CAN_TO_STRING, 0 , FALSE );
  8403. // Remove item...
  8404. DeleteObj( &(pSoldier->inv[ HANDPOS ] ) );
  8405. fInterfacePanelDirty = DIRTYLEVEL2;
  8406. }
  8407. void EVENT_SoldierBeginReloadRobot( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection, UINT8 ubMercSlot )
  8408. {
  8409. UINT8 ubPerson;
  8410. // Make sure we have a robot here....
  8411. ubPerson = WhoIsThere2( sGridNo, pSoldier->bLevel );
  8412. if ( ubPerson != NOBODY && MercPtrs[ ubPerson ]->uiStatusFlags & SOLDIER_ROBOT )
  8413. {
  8414. // CHANGE DIRECTION AND GOTO ANIMATION NOW
  8415. EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
  8416. EVENT_SetSoldierDirection( pSoldier, ubDirection );
  8417. // CHANGE TO ANIMATION
  8418. EVENT_InitNewSoldierAnim( pSoldier, RELOAD_ROBOT, 0 , FALSE );
  8419. }
  8420. }
  8421. void ResetSoldierChangeStatTimer( SOLDIERTYPE *pSoldier )
  8422. {
  8423. pSoldier->uiChangeLevelTime = 0;
  8424. pSoldier->uiChangeHealthTime = 0;
  8425. pSoldier->uiChangeStrengthTime= 0;
  8426. pSoldier->uiChangeDexterityTime= 0;
  8427. pSoldier->uiChangeAgilityTime= 0;
  8428. pSoldier->uiChangeWisdomTime= 0;
  8429. pSoldier->uiChangeLeadershipTime= 0;
  8430. pSoldier->uiChangeMarksmanshipTime= 0;
  8431. pSoldier->uiChangeExplosivesTime= 0;
  8432. pSoldier->uiChangeMedicalTime= 0;
  8433. pSoldier->uiChangeMechanicalTime= 0;
  8434. return;
  8435. }
  8436. void ChangeToFlybackAnimation( SOLDIERTYPE *pSoldier, INT8 bDirection )
  8437. {
  8438. UINT16 usNewGridNo;
  8439. // Get dest gridno, convert to center coords
  8440. usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) );
  8441. usNewGridNo = NewGridNo( (UINT16)usNewGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) );
  8442. // Remove any previous actions
  8443. pSoldier->ubPendingAction = NO_PENDING_ACTION;
  8444. // Set path....
  8445. pSoldier->usPathDataSize = 0;
  8446. pSoldier->usPathIndex = 0;
  8447. pSoldier->usPathingData[ pSoldier->usPathDataSize ] = gOppositeDirection[ pSoldier->bDirection ];
  8448. pSoldier->usPathDataSize++;
  8449. pSoldier->usPathingData[ pSoldier->usPathDataSize ] = gOppositeDirection[ pSoldier->bDirection ];
  8450. pSoldier->usPathDataSize++;
  8451. pSoldier->sFinalDestination = usNewGridNo;
  8452. EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, FLYBACK_HIT );
  8453. // Get a new direction based on direction
  8454. EVENT_InitNewSoldierAnim( pSoldier, FLYBACK_HIT, 0 , FALSE );
  8455. }
  8456. void ChangeToFallbackAnimation( SOLDIERTYPE *pSoldier, INT8 bDirection )
  8457. {
  8458. UINT16 usNewGridNo;
  8459. // Get dest gridno, convert to center coords
  8460. usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) );
  8461. //usNewGridNo = NewGridNo( (UINT16)usNewGridNo, (UINT16)(-1 * DirectionInc( bDirection ) ) );
  8462. // Remove any previous actions
  8463. pSoldier->ubPendingAction = NO_PENDING_ACTION;
  8464. // Set path....
  8465. pSoldier->usPathDataSize = 0;
  8466. pSoldier->usPathIndex = 0;
  8467. pSoldier->usPathingData[ pSoldier->usPathDataSize ] = gOppositeDirection[ pSoldier->bDirection ];
  8468. pSoldier->usPathDataSize++;
  8469. pSoldier->sFinalDestination = usNewGridNo;
  8470. EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, FALLBACK_HIT_STAND );
  8471. // Get a new direction based on direction
  8472. EVENT_InitNewSoldierAnim( pSoldier, FALLBACK_HIT_STAND, 0 , FALSE );
  8473. }
  8474. void SetSoldierCowerState( SOLDIERTYPE *pSoldier, BOOLEAN fOn )
  8475. {
  8476. // Robot's don't cower!
  8477. if ( pSoldier->ubBodyType == ROBOTNOWEAPON )
  8478. {
  8479. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("ERROR: Robot was told to cower!" ) );
  8480. return;
  8481. }
  8482. // OK< set flag and do anim...
  8483. if ( fOn )
  8484. {
  8485. if ( !( pSoldier->uiStatusFlags & SOLDIER_COWERING ) )
  8486. {
  8487. EVENT_InitNewSoldierAnim( pSoldier, START_COWER, 0, FALSE );
  8488. pSoldier->uiStatusFlags |= SOLDIER_COWERING;
  8489. pSoldier->ubDesiredHeight = ANIM_CROUCH;
  8490. }
  8491. }
  8492. else
  8493. {
  8494. if ( (pSoldier->uiStatusFlags & SOLDIER_COWERING) )
  8495. {
  8496. EVENT_InitNewSoldierAnim( pSoldier, END_COWER, 0, FALSE );
  8497. pSoldier->uiStatusFlags &= (~SOLDIER_COWERING );
  8498. pSoldier->ubDesiredHeight = ANIM_STAND;
  8499. }
  8500. }
  8501. }
  8502. void MercStealFromMerc( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTarget )
  8503. {
  8504. INT16 sActionGridNo, sGridNo, sAdjustedGridNo;
  8505. UINT8 ubDirection;
  8506. // OK, find an adjacent gridno....
  8507. sGridNo = pTarget->sGridNo;
  8508. // See if we can get there to punch
  8509. sActionGridNo = FindAdjacentGridEx( pSoldier, sGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
  8510. if ( sActionGridNo != -1 )
  8511. {
  8512. // SEND PENDING ACTION
  8513. pSoldier->ubPendingAction = MERC_STEAL;
  8514. pSoldier->sPendingActionData2 = pTarget->sGridNo;
  8515. pSoldier->bPendingActionData3 = ubDirection;
  8516. pSoldier->ubPendingActionAnimCount = 0;
  8517. // CHECK IF WE ARE AT THIS GRIDNO NOW
  8518. if ( pSoldier->sGridNo != sActionGridNo )
  8519. {
  8520. // WALK UP TO DEST FIRST
  8521. SendGetNewSoldierPathEvent( pSoldier, sActionGridNo, pSoldier->usUIMovementMode );
  8522. }
  8523. else
  8524. {
  8525. EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
  8526. EVENT_InitNewSoldierAnim( pSoldier, STEAL_ITEM, 0 , FALSE );
  8527. }
  8528. // OK, set UI
  8529. gTacticalStatus.ubAttackBusyCount++;
  8530. // reset attacking item (hand)
  8531. pSoldier->usAttackingWeapon = 0;
  8532. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting STEAL attack, attack count now %d", gTacticalStatus.ubAttackBusyCount) );
  8533. SetUIBusy( pSoldier->ubID );
  8534. }
  8535. }
  8536. BOOLEAN PlayerSoldierStartTalking( SOLDIERTYPE *pSoldier, UINT8 ubTargetID, BOOLEAN fValidate )
  8537. {
  8538. INT16 sFacingDir, sXPos, sYPos, sAPCost;
  8539. SOLDIERTYPE *pTSoldier;
  8540. UINT32 uiRange;
  8541. if ( ubTargetID == NOBODY )
  8542. {
  8543. return( FALSE );
  8544. }
  8545. pTSoldier = MercPtrs[ ubTargetID ];
  8546. // Check distance again, to be sure
  8547. if ( fValidate )
  8548. {
  8549. // OK, since we locked this guy from moving
  8550. // we should be close enough, so talk ( unless he is now dead )
  8551. if ( !IsValidTalkableNPC( (UINT8)ubTargetID, FALSE, FALSE, FALSE ) )
  8552. {
  8553. return( FALSE );
  8554. }
  8555. uiRange = GetRangeFromGridNoDiff( pSoldier->sGridNo, pTSoldier->sGridNo );
  8556. if ( uiRange > ( NPC_TALK_RADIUS * 2 ) )
  8557. {
  8558. // Todo here - should we follow dude?
  8559. return( FALSE );
  8560. }
  8561. }
  8562. // Get APs...
  8563. sAPCost = AP_TALK;
  8564. // Deduct points from our guy....
  8565. DeductPoints( pSoldier, sAPCost, 0 );
  8566. ConvertGridNoToXY( pTSoldier->sGridNo, &sXPos, &sYPos );
  8567. // Get direction from mouse pos
  8568. sFacingDir = GetDirectionFromXY( sXPos, sYPos, pSoldier );
  8569. // Set our guy facing
  8570. SendSoldierSetDesiredDirectionEvent( pSoldier, sFacingDir );
  8571. // Set NPC facing
  8572. SendSoldierSetDesiredDirectionEvent( pTSoldier, gOppositeDirection[ sFacingDir ] );
  8573. // Stop our guys...
  8574. EVENT_StopMerc( pSoldier, pSoldier->sGridNo, pSoldier->bDirection );
  8575. // ATE; Check for normal civs...
  8576. if ( GetCivType( pTSoldier ) != CIV_TYPE_NA )
  8577. {
  8578. StartCivQuote( pTSoldier );
  8579. return( FALSE );
  8580. }
  8581. // Are we an EPC that is being escorted?
  8582. if ( pTSoldier->ubProfile != NO_PROFILE && pTSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC )
  8583. {
  8584. return( InitiateConversation( pTSoldier, pSoldier, APPROACH_EPC_WHO_IS_RECRUITED, 0 ) );
  8585. //Converse( pTSoldier->ubProfile, pSoldier->ubProfile, APPROACH_EPC_WHO_IS_RECRUITED, 0 );
  8586. }
  8587. else if (pTSoldier->bNeutral)
  8588. {
  8589. switch( pTSoldier->ubProfile )
  8590. {
  8591. case JIM:
  8592. case JACK:
  8593. case OLAF:
  8594. case RAY:
  8595. case OLGA:
  8596. case TYRONE:
  8597. // Start combat etc
  8598. DeleteTalkingMenu();
  8599. CancelAIAction( pTSoldier, TRUE );
  8600. AddToShouldBecomeHostileOrSayQuoteList( pTSoldier->ubID );
  8601. break;
  8602. default:
  8603. // Start talking!
  8604. return( InitiateConversation( pTSoldier, pSoldier, NPC_INITIAL_QUOTE, 0 ) );
  8605. break;
  8606. }
  8607. }
  8608. else
  8609. {
  8610. // Start talking with hostile NPC
  8611. return( InitiateConversation( pTSoldier, pSoldier, APPROACH_ENEMY_NPC_QUOTE, 0 ) );
  8612. }
  8613. return( TRUE );
  8614. }
  8615. BOOLEAN IsValidSecondHandShot( SOLDIERTYPE *pSoldier )
  8616. {
  8617. if ( Item[ pSoldier->inv[ SECONDHANDPOS ].usItem ].usItemClass == IC_GUN &&
  8618. !(Item[ pSoldier->inv[SECONDHANDPOS ].usItem ].fFlags & ITEM_TWO_HANDED) &&
  8619. !pSoldier->bDoBurst &&
  8620. pSoldier->inv[ HANDPOS ].usItem != GLAUNCHER &&
  8621. Item[ pSoldier->inv[HANDPOS].usItem ].usItemClass == IC_GUN &&
  8622. pSoldier->inv[SECONDHANDPOS].bGunStatus >= USABLE &&
  8623. pSoldier->inv[SECONDHANDPOS].ubGunShotsLeft > 0 )
  8624. {
  8625. return( TRUE );
  8626. }
  8627. return( FALSE );
  8628. }
  8629. BOOLEAN IsValidSecondHandShotForReloadingPurposes( SOLDIERTYPE *pSoldier )
  8630. {
  8631. // should be maintained as same as function above with line
  8632. // about ammo taken out!
  8633. if ( Item[ pSoldier->inv[ SECONDHANDPOS ].usItem ].usItemClass == IC_GUN &&
  8634. !pSoldier->bDoBurst &&
  8635. pSoldier->inv[ HANDPOS ].usItem != GLAUNCHER &&
  8636. Item[ pSoldier->inv[HANDPOS].usItem ].usItemClass == IC_GUN &&
  8637. pSoldier->inv[SECONDHANDPOS].bGunStatus >= USABLE //&&
  8638. // pSoldier->inv[SECONDHANDPOS].ubGunShotsLeft > 0 &&
  8639. // gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_PRONE )
  8640. )
  8641. {
  8642. return( TRUE );
  8643. }
  8644. return( FALSE );
  8645. }
  8646. BOOLEAN CanRobotBeControlled( SOLDIERTYPE *pSoldier )
  8647. {
  8648. SOLDIERTYPE *pController;
  8649. if ( !( pSoldier->uiStatusFlags & SOLDIER_ROBOT ) )
  8650. {
  8651. return( FALSE );
  8652. }
  8653. if ( pSoldier->ubRobotRemoteHolderID == NOBODY )
  8654. {
  8655. return( FALSE );
  8656. }
  8657. pController = MercPtrs[ pSoldier->ubRobotRemoteHolderID ];
  8658. if ( pController->bActive )
  8659. {
  8660. if ( ControllingRobot( pController ) )
  8661. {
  8662. // ALL'S OK!
  8663. return( TRUE );
  8664. }
  8665. }
  8666. return( FALSE );
  8667. }
  8668. BOOLEAN ControllingRobot( SOLDIERTYPE *pSoldier )
  8669. {
  8670. SOLDIERTYPE *pRobot;
  8671. INT8 bPos;
  8672. if ( !pSoldier->bActive )
  8673. {
  8674. return( FALSE );
  8675. }
  8676. // EPCs can't control the robot (no inventory to hold remote, for one)
  8677. if ( AM_AN_EPC( pSoldier ) )
  8678. {
  8679. return( FALSE );
  8680. }
  8681. // Don't require pSoldier->bInSector here, it must work from mapscreen!
  8682. // are we in ok shape?
  8683. if ( pSoldier->bLife < OKLIFE || ( pSoldier->bTeam != gbPlayerNum ) )
  8684. {
  8685. return( FALSE );
  8686. }
  8687. // allow control from within vehicles - allows strategic travel in a vehicle with robot!
  8688. if ( ( pSoldier->bAssignment >= ON_DUTY ) && ( pSoldier->bAssignment != VEHICLE ) )
  8689. {
  8690. return( FALSE );
  8691. }
  8692. // is the soldier wearing a robot remote control?
  8693. bPos = FindObj( pSoldier, ROBOT_REMOTE_CONTROL );
  8694. if ( bPos != HEAD1POS && bPos != HEAD2POS )
  8695. {
  8696. return( FALSE );
  8697. }
  8698. // Find the robot
  8699. pRobot = FindSoldierByProfileID( ROBOT, TRUE );
  8700. if ( !pRobot )
  8701. {
  8702. return( FALSE );
  8703. }
  8704. if ( pRobot->bActive )
  8705. {
  8706. // Are we in the same sector....?
  8707. // ARM: CHANGED TO WORK IN MAPSCREEN, DON'T USE WorldSector HERE
  8708. if ( pRobot->sSectorX == pSoldier->sSectorX &&
  8709. pRobot->sSectorY == pSoldier->sSectorY &&
  8710. pRobot->bSectorZ == pSoldier->bSectorZ )
  8711. {
  8712. // they have to be either both in sector, or both on the road
  8713. if ( pRobot->fBetweenSectors == pSoldier->fBetweenSectors )
  8714. {
  8715. // if they're on the road...
  8716. if ( pRobot->fBetweenSectors )
  8717. {
  8718. // they have to be in the same squad or vehicle
  8719. if ( pRobot->bAssignment != pSoldier->bAssignment )
  8720. {
  8721. return( FALSE );
  8722. }
  8723. // if in a vehicle, must be the same vehicle
  8724. if ( pRobot->bAssignment == VEHICLE && ( pRobot->iVehicleId != pSoldier->iVehicleId ) )
  8725. {
  8726. return( FALSE );
  8727. }
  8728. }
  8729. // all OK!
  8730. return( TRUE );
  8731. }
  8732. }
  8733. }
  8734. return( FALSE );
  8735. }
  8736. SOLDIERTYPE *GetRobotController( SOLDIERTYPE *pSoldier )
  8737. {
  8738. if ( pSoldier->ubRobotRemoteHolderID == NOBODY )
  8739. {
  8740. return( NULL );
  8741. }
  8742. else
  8743. {
  8744. return( MercPtrs[ pSoldier->ubRobotRemoteHolderID ] );
  8745. }
  8746. }
  8747. void UpdateRobotControllerGivenRobot( SOLDIERTYPE *pRobot )
  8748. {
  8749. SOLDIERTYPE *pTeamSoldier;
  8750. INT32 cnt = 0;
  8751. // Loop through guys and look for a controller!
  8752. // set up soldier ptr as first element in mercptrs list
  8753. cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
  8754. // run through list
  8755. for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++, pTeamSoldier++)
  8756. {
  8757. if ( pTeamSoldier->bActive )
  8758. {
  8759. if ( ControllingRobot( pTeamSoldier ) )
  8760. {
  8761. pRobot->ubRobotRemoteHolderID = pTeamSoldier->ubID;
  8762. return;
  8763. }
  8764. }
  8765. }
  8766. pRobot->ubRobotRemoteHolderID = NOBODY;
  8767. }
  8768. void UpdateRobotControllerGivenController( SOLDIERTYPE *pSoldier )
  8769. {
  8770. SOLDIERTYPE *pTeamSoldier;
  8771. INT32 cnt = 0;
  8772. // First see if are still controlling the robot
  8773. if ( !ControllingRobot( pSoldier ) )
  8774. {
  8775. return;
  8776. }
  8777. // set up soldier ptr as first element in mercptrs list
  8778. cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
  8779. // Loop through guys to find the robot....
  8780. for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++, pTeamSoldier++)
  8781. {
  8782. if ( pTeamSoldier->bActive && ( pTeamSoldier->uiStatusFlags & SOLDIER_ROBOT ) )
  8783. {
  8784. pTeamSoldier->ubRobotRemoteHolderID = pSoldier->ubID;
  8785. }
  8786. }
  8787. }
  8788. void HandleSoldierTakeDamageFeedback( SOLDIERTYPE *pSoldier )
  8789. {
  8790. // Do sound.....
  8791. // if ( pSoldier->bLife >= CONSCIOUSNESS )
  8792. {
  8793. // ATE: Limit how often we grunt...
  8794. if ( ( GetJA2Clock( ) - pSoldier->uiTimeSinceLastBleedGrunt ) > 1000 )
  8795. {
  8796. pSoldier->uiTimeSinceLastBleedGrunt = GetJA2Clock( );
  8797. DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_HIT1 + Random( 2 ) ) );
  8798. }
  8799. }
  8800. // Flash portrait....
  8801. pSoldier->fFlashPortrait = TRUE;
  8802. pSoldier->bFlashPortraitFrame = FLASH_PORTRAIT_STARTSHADE;
  8803. RESETTIMECOUNTER( pSoldier->PortraitFlashCounter, FLASH_PORTRAIT_DELAY );
  8804. }
  8805. void HandleSystemNewAISituation( SOLDIERTYPE *pSoldier, BOOLEAN fResetABC )
  8806. {
  8807. // Are we an AI guy?
  8808. if ( gTacticalStatus.ubCurrentTeam != gbPlayerNum && pSoldier->bTeam != gbPlayerNum )
  8809. {
  8810. if ( pSoldier->bNewSituation == IS_NEW_SITUATION )
  8811. {
  8812. // Cancel what they were doing....
  8813. pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
  8814. pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION;
  8815. pSoldier->fTurningFromPronePosition = FALSE;
  8816. pSoldier->ubPendingDirection = NO_PENDING_DIRECTION;
  8817. pSoldier->ubPendingAction = NO_PENDING_ACTION;
  8818. pSoldier->bEndDoorOpenCode = 0;
  8819. // if this guy isn't under direct AI control, WHO GIVES A FLYING FLICK?
  8820. if ( pSoldier->uiStatusFlags & SOLDIER_UNDERAICONTROL )
  8821. {
  8822. if ( pSoldier->fTurningToShoot )
  8823. {
  8824. pSoldier->fTurningToShoot = FALSE;
  8825. // Release attacker
  8826. // OK - this is hightly annoying , but due to the huge combinations of
  8827. // things that can happen - 1 of them is that sLastTarget will get unset
  8828. // after turn is done - so set flag here to tell it not to...
  8829. pSoldier->fDontUnsetLastTargetFromTurn = TRUE;
  8830. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending fire because saw something: DONE IN SYSTEM NEW SITUATION") );
  8831. ReduceAttackBusyCount( pSoldier->ubID, FALSE );
  8832. }
  8833. if ( pSoldier->pTempObject != NULL )
  8834. {
  8835. // Place it back into inv....
  8836. AutoPlaceObject( pSoldier, pSoldier->pTempObject, FALSE );
  8837. MemFree( pSoldier->pTempObject );
  8838. pSoldier->pTempObject = NULL;
  8839. pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
  8840. pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION;
  8841. // Decrement attack counter...
  8842. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending throw because saw something: DONE IN SYSTEM NEW SITUATION") );
  8843. ReduceAttackBusyCount( pSoldier->ubID, FALSE );
  8844. }
  8845. }
  8846. }
  8847. }
  8848. }
  8849. void InternalPlaySoldierFootstepSound( SOLDIERTYPE * pSoldier )
  8850. {
  8851. UINT8 ubRandomSnd;
  8852. INT8 bVolume = MIDVOLUME;
  8853. // Assume outside
  8854. UINT32 ubSoundBase = WALK_LEFT_OUT;
  8855. UINT8 ubRandomMax = 4;
  8856. // Determine if we are on the floor
  8857. if ( !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
  8858. {
  8859. if ( pSoldier->usAnimState == HOPFENCE )
  8860. {
  8861. bVolume = HIGHVOLUME;
  8862. }
  8863. if ( pSoldier->uiStatusFlags & SOLDIER_ROBOT )
  8864. {
  8865. PlaySoldierJA2Sample( pSoldier->ubID, ROBOT_BEEP, RATE_11025, SoundVolume( bVolume, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ), TRUE );
  8866. return;
  8867. }
  8868. //if ( SoldierOnScreen( pSoldier->ubID ) )
  8869. {
  8870. if ( pSoldier->usAnimState == CRAWLING )
  8871. {
  8872. ubSoundBase = CRAWL_1;
  8873. }
  8874. else
  8875. {
  8876. // Pick base based on terrain over....
  8877. if ( pSoldier->bOverTerrainType == FLAT_FLOOR )
  8878. {
  8879. ubSoundBase = WALK_LEFT_IN;
  8880. }
  8881. else if ( pSoldier->bOverTerrainType == DIRT_ROAD || pSoldier->bOverTerrainType == PAVED_ROAD )
  8882. {
  8883. ubSoundBase = WALK_LEFT_ROAD;
  8884. }
  8885. else if ( pSoldier->bOverTerrainType == LOW_WATER || pSoldier->bOverTerrainType == MED_WATER )
  8886. {
  8887. ubSoundBase = WATER_WALK1_IN;
  8888. ubRandomMax = 2;
  8889. }
  8890. else if ( pSoldier->bOverTerrainType == DEEP_WATER )
  8891. {
  8892. ubSoundBase = SWIM_1;
  8893. ubRandomMax = 2;
  8894. }
  8895. }
  8896. // Pick a random sound...
  8897. do
  8898. {
  8899. ubRandomSnd = (UINT8)Random( ubRandomMax );
  8900. } while ( ubRandomSnd == pSoldier->ubLastFootPrintSound );
  8901. pSoldier->ubLastFootPrintSound = ubRandomSnd;
  8902. // OK, if in realtime, don't play at full volume, because too many people walking around
  8903. // sounds don't sound good - ( unless we are the selected guy, then always play at reg volume )
  8904. if ( ! ( gTacticalStatus.uiFlags & INCOMBAT ) && ( pSoldier->ubID != gusSelectedSoldier ) )
  8905. {
  8906. bVolume = LOWVOLUME;
  8907. }
  8908. PlaySoldierJA2Sample( pSoldier->ubID, ubSoundBase + pSoldier->ubLastFootPrintSound, RATE_11025, SoundVolume( bVolume, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ), TRUE );
  8909. }
  8910. }
  8911. }
  8912. void PlaySoldierFootstepSound( SOLDIERTYPE *pSoldier )
  8913. {
  8914. // normally, not in stealth mode
  8915. if ( !pSoldier->bStealthMode )
  8916. {
  8917. InternalPlaySoldierFootstepSound( pSoldier );
  8918. }
  8919. }
  8920. void PlayStealthySoldierFootstepSound( SOLDIERTYPE *pSoldier )
  8921. {
  8922. // even if in stealth mode
  8923. InternalPlaySoldierFootstepSound( pSoldier );
  8924. }
  8925. void CrowsFlyAway( UINT8 ubTeam )
  8926. {
  8927. UINT32 cnt;
  8928. SOLDIERTYPE *pTeamSoldier;
  8929. for ( cnt = gTacticalStatus.Team[ ubTeam ].bFirstID, pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ ubTeam ].bLastID; cnt++, pTeamSoldier++ )
  8930. {
  8931. if ( pTeamSoldier->bActive && pTeamSoldier->bInSector )
  8932. {
  8933. if ( pTeamSoldier->ubBodyType == CROW && pTeamSoldier->usAnimState != CROW_FLY )
  8934. {
  8935. // fly away even if not seen!
  8936. HandleCrowFlyAway( pTeamSoldier );
  8937. }
  8938. }
  8939. }
  8940. }
  8941. #ifdef JA2BETAVERSION
  8942. void DebugValidateSoldierData( )
  8943. {
  8944. UINT32 cnt;
  8945. SOLDIERTYPE *pSoldier;
  8946. CHAR16 sString[ 1024 ];
  8947. BOOLEAN fProblemDetected = FALSE;
  8948. static uiFrameCount = 0;
  8949. // this function is too slow to run every frame, so do the check only every 50 frames
  8950. if ( uiFrameCount++ < 50 )
  8951. {
  8952. return;
  8953. }
  8954. // reset frame counter
  8955. uiFrameCount = 0;
  8956. // Loop through our team...
  8957. cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
  8958. for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pSoldier++)
  8959. {
  8960. if ( pSoldier->bActive )
  8961. {
  8962. // OK, first check for alive people
  8963. // Don't do this check if we are a vehicle...
  8964. if ( pSoldier->bLife > 0 && !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
  8965. {
  8966. // Alive -- now check for proper group IDs
  8967. if ( pSoldier->ubGroupID == 0 && pSoldier->bAssignment != IN_TRANSIT && pSoldier->bAssignment != ASSIGNMENT_POW && !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) )
  8968. {
  8969. // This is bad!
  8970. swprintf( sString, L"Soldier Data Error: Soldier %d is alive but has a zero group ID.", cnt );
  8971. fProblemDetected = TRUE;
  8972. }
  8973. else if ( ( pSoldier->ubGroupID != 0 ) && ( GetGroup( pSoldier->ubGroupID ) == NULL ) )
  8974. {
  8975. // This is bad!
  8976. swprintf( sString, L"Soldier Data Error: Soldier %d has an invalid group ID of %d.", cnt, pSoldier->ubGroupID );
  8977. fProblemDetected = TRUE;
  8978. }
  8979. }
  8980. else
  8981. {
  8982. if ( pSoldier->ubGroupID != 0 && ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) )
  8983. {
  8984. // Dead guys should have 0 group IDs
  8985. //swprintf( sString, L"GroupID Error: Soldier %d is dead but has a non-zero group ID.", cnt );
  8986. //fProblemDetected = TRUE;
  8987. }
  8988. }
  8989. // check for invalid sector data
  8990. if ( ( pSoldier->bAssignment != IN_TRANSIT ) &&
  8991. ( ( pSoldier->sSectorX <= 0 ) || ( pSoldier->sSectorX >= 17 ) ||
  8992. ( pSoldier->sSectorY <= 0 ) || ( pSoldier->sSectorY >= 17 ) ||
  8993. ( pSoldier->bSectorZ < 0 ) || ( pSoldier->bSectorZ > 3 ) ) )
  8994. {
  8995. swprintf( sString, L"Soldier Data Error: Soldier %d is located at %d/%d/%d.", cnt, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
  8996. fProblemDetected = TRUE;
  8997. }
  8998. }
  8999. if ( fProblemDetected )
  9000. {
  9001. SAIReportError( sString );
  9002. /*
  9003. if ( guiCurrentScreen == MAP_SCREEN )
  9004. DoMapMessageBox( MSG_BOX_BASIC_STYLE, sString, MAP_SCREEN, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback );
  9005. else
  9006. DoMessageBox( MSG_BOX_BASIC_STYLE, sString, GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_OK, NULL, NULL );
  9007. */
  9008. break;
  9009. }
  9010. }
  9011. // also do this
  9012. ValidatePlayersAreInOneGroupOnly();
  9013. }
  9014. #endif
  9015. void BeginTyingToFall( SOLDIERTYPE *pSoldier )
  9016. {
  9017. pSoldier->bStartFallDir = pSoldier->bDirection;
  9018. pSoldier->fTryingToFall = TRUE;
  9019. // Randomize direction
  9020. if ( Random( 50 ) < 25 )
  9021. {
  9022. pSoldier->fFallClockwise = TRUE;
  9023. }
  9024. else
  9025. {
  9026. pSoldier->fFallClockwise = FALSE;
  9027. }
  9028. }
  9029. void SetSoldierAsUnderAiControl( SOLDIERTYPE *pSoldierToSet )
  9030. {
  9031. SOLDIERTYPE *pSoldier=NULL;
  9032. INT32 cnt;
  9033. if ( pSoldierToSet == NULL )
  9034. {
  9035. return;
  9036. }
  9037. // Loop through ALL teams...
  9038. cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID;
  9039. for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ LAST_TEAM ].bLastID; cnt++,pSoldier++)
  9040. {
  9041. if( pSoldier->bActive )
  9042. {
  9043. pSoldier->uiStatusFlags &= ~SOLDIER_UNDERAICONTROL;
  9044. }
  9045. }
  9046. pSoldierToSet->uiStatusFlags |= SOLDIER_UNDERAICONTROL;
  9047. }
  9048. void HandlePlayerTogglingLightEffects( BOOLEAN fToggleValue )
  9049. {
  9050. if( fToggleValue )
  9051. {
  9052. //Toggle light status
  9053. gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] ^= TRUE;
  9054. }
  9055. //Update all the mercs in the sector
  9056. EnableDisableSoldierLightEffects( gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] );
  9057. SetRenderFlags(RENDER_FLAG_FULL);
  9058. }
  9059. void EnableDisableSoldierLightEffects( BOOLEAN fEnableLights )
  9060. {
  9061. SOLDIERTYPE *pSoldier=NULL;
  9062. INT32 cnt;
  9063. // Loop through player teams...
  9064. cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID;
  9065. for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++,pSoldier++)
  9066. {
  9067. //if the soldier is in the sector
  9068. if( pSoldier->bActive && pSoldier->bInSector && pSoldier->bLife >= OKLIFE )
  9069. {
  9070. //if we are to enable the lights
  9071. if( fEnableLights )
  9072. {
  9073. //Add the light around the merc
  9074. PositionSoldierLight( pSoldier );
  9075. }
  9076. else
  9077. {
  9078. //Delete the fake light the merc casts
  9079. DeleteSoldierLight( pSoldier );
  9080. //Light up the merc though
  9081. SetSoldierPersonalLightLevel( pSoldier );
  9082. }
  9083. }
  9084. }
  9085. }
  9086. void SetSoldierPersonalLightLevel( SOLDIERTYPE *pSoldier )
  9087. {
  9088. if( pSoldier == NULL )
  9089. {
  9090. return;
  9091. }
  9092. if( pSoldier->sGridNo == NOWHERE )
  9093. {
  9094. return;
  9095. }
  9096. //THe light level for the soldier
  9097. gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubShadeLevel = 3;
  9098. gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubSumLights = 5;
  9099. gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubMaxLights = 5;
  9100. gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubNaturalShadeLevel = 5;
  9101. }