Handle Items.c 136 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167
  1. #ifdef PRECOMPILEDHEADERS
  2. #include "Tactical All.h"
  3. #else
  4. #include "items.h"
  5. #include "Action Items.h"
  6. #include "handle Items.h"
  7. #include "overhead.h"
  8. #include "weapons.h"
  9. #include "points.h"
  10. #include "tiledef.h"
  11. #include "worlddef.h"
  12. #include "worldman.h"
  13. #include "interface.h"
  14. #include "renderworld.h"
  15. #include "Animation Control.h"
  16. #include "font control.h"
  17. #include "render dirty.h"
  18. #include "World items.h"
  19. #include "text.h"
  20. #include "Timer Control.h"
  21. #include "wcheck.h"
  22. #include "interface items.h"
  23. #include "physics.h"
  24. #include "soldier profile.h"
  25. #include "interface dialogue.h"
  26. #include "quests.h"
  27. #include "message.h"
  28. #include "isometric utils.h"
  29. #include "los.h"
  30. #include "dialogue control.h"
  31. #include "ai.h"
  32. #include "soldier macros.h"
  33. #include "interface panels.h"
  34. #include "Strategic Town Loyalty.h"
  35. #include "soldier functions.h"
  36. #include "Map Screen Helicopter.h"
  37. #include "pathai.h"
  38. #include "fov.h"
  39. #include "MessageBoxScreen.h"
  40. #include "explosion control.h"
  41. #include "SkillCheck.h"
  42. #include "Campaign.h"
  43. #include "Random.h"
  44. #include "structure wrap.h"
  45. #include "interactive tiles.h"
  46. #include "SaveLoadMap.h"
  47. #include "ShopKeeper Interface.h"
  48. #include "Arms Dealer Init.h"
  49. #include "soldier add.h"
  50. #include "sound control.h"
  51. #include "squads.h"
  52. #include "rotting corpses.h"
  53. #include "soldier ani.h"
  54. #include "Opplist.h"
  55. #include "qarray.h"
  56. #include "render fun.h"
  57. #include "environment.h"
  58. #endif
  59. #define NUM_ITEMS_LISTED 8
  60. #define NUM_ITEM_FLASH_SLOTS 50
  61. #define MIN_LOB_RANGE 6
  62. ITEM_POOL_LOCATOR FlashItemSlots[ NUM_ITEM_FLASH_SLOTS ];
  63. UINT32 guiNumFlashItemSlots = 0;
  64. LEVELNODE *AddItemGraphicToWorld( INVTYPE *pItem, INT16 sGridNo, UINT8 ubLevel );
  65. INT8 GetListMouseHotSpot( INT16 sLargestLineWidth, INT8 bNumItemsListed, INT16 sFontX, INT16 sFontY, INT8 bCurStart );
  66. void RemoveItemGraphicFromWorld( INVTYPE *pItem, INT16 sGridNo, UINT8 ubLevel, LEVELNODE *pLevelNode );
  67. ITEM_POOL * GetItemPoolForIndex( INT16 sGridNo, INT32 iItemIndex, UINT8 ubLevel );
  68. INT32 GetFreeFlashItemSlot(void);
  69. void RecountFlashItemSlots(void);
  70. INT32 AddFlashItemSlot( ITEM_POOL *pItemPool, ITEM_POOL_LOCATOR_HOOK Callback, UINT8 ubFlags );
  71. BOOLEAN RemoveFlashItemSlot( ITEM_POOL *pItemPool );
  72. // Disgusting hacks: have to keep track of these values for accesses in callbacks
  73. static SOLDIERTYPE * gpTempSoldier;
  74. static INT16 gsTempGridno;
  75. static INT8 bTempFrequency;
  76. void BombMessageBoxCallBack( UINT8 ubExitValue );
  77. void BoobyTrapMessageBoxCallBack( UINT8 ubExitValue );
  78. void SwitchMessageBoxCallBack( UINT8 ubExitValue );
  79. void BoobyTrapDialogueCallBack( void );
  80. void MineSpottedDialogueCallBack( void );
  81. void MineSpottedLocatorCallback( void );
  82. void RemoveBlueFlagDialogueCallBack( UINT8 ubExitValue );
  83. void MineSpottedMessageBoxCallBack( UINT8 ubExitValue );
  84. void CheckForPickedOwnership( void );
  85. void BoobyTrapInMapScreenMessageBoxCallBack( UINT8 ubExitValue );
  86. BOOLEAN ContinuePastBoobyTrap( SOLDIERTYPE * pSoldier, INT16 sGridNo, INT8 bLevel, INT32 iItemIndex, BOOLEAN fInStrategic, BOOLEAN *pfSaidQuote );
  87. extern BOOLEAN ItemIsCool( OBJECTTYPE * pObj );
  88. extern INT8 gbItemPointerSrcSlot;
  89. extern void MAPEndItemPointer( );
  90. extern BOOLEAN gfResetUIMovementOptimization;
  91. BOOLEAN ItemPoolOKForPickup( SOLDIERTYPE * pSoldier, ITEM_POOL *pItemPool, INT8 bZLevel );
  92. SOLDIERTYPE * gpBoobyTrapSoldier;
  93. ITEM_POOL * gpBoobyTrapItemPool;
  94. INT16 gsBoobyTrapGridNo;
  95. INT8 gbBoobyTrapLevel;
  96. BOOLEAN gfDisarmingBuriedBomb;
  97. extern BOOLEAN gfDontChargeAPsToPickup;
  98. INT8 gbTrapDifficulty;
  99. BOOLEAN gfJustFoundBoobyTrap = FALSE;
  100. void StartBombMessageBox( SOLDIERTYPE * pSoldier, INT16 sGridNo );
  101. BOOLEAN HandleCheckForBadChangeToGetThrough( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTargetSoldier, INT16 sTargetGridNo , INT8 bLevel )
  102. {
  103. BOOLEAN fBadChangeToGetThrough = FALSE;
  104. if ( pTargetSoldier != NULL )
  105. {
  106. if ( SoldierToSoldierBodyPartChanceToGetThrough( pSoldier, pTargetSoldier, pSoldier->bAimShotLocation ) < OK_CHANCE_TO_GET_THROUGH )
  107. {
  108. fBadChangeToGetThrough = TRUE;
  109. }
  110. }
  111. else
  112. {
  113. if ( SoldierToLocationChanceToGetThrough( pSoldier, sTargetGridNo, (INT8) bLevel, 0, NOBODY ) < OK_CHANCE_TO_GET_THROUGH )
  114. {
  115. fBadChangeToGetThrough = TRUE;
  116. }
  117. }
  118. if ( fBadChangeToGetThrough )
  119. {
  120. if ( gTacticalStatus.sCantGetThroughSoldierGridNo != pSoldier->sGridNo || gTacticalStatus.sCantGetThroughGridNo != sTargetGridNo || gTacticalStatus.ubCantGetThroughID != pSoldier->ubID )
  121. {
  122. gTacticalStatus.fCantGetThrough = FALSE;
  123. }
  124. // Have we done this once already?
  125. if ( !gTacticalStatus.fCantGetThrough )
  126. {
  127. gTacticalStatus.fCantGetThrough = TRUE;
  128. gTacticalStatus.sCantGetThroughGridNo = sTargetGridNo;
  129. gTacticalStatus.ubCantGetThroughID = pSoldier->ubID;
  130. gTacticalStatus.sCantGetThroughSoldierGridNo = pSoldier->sGridNo;
  131. // PLay quote
  132. TacticalCharacterDialogue( pSoldier, QUOTE_NO_LINE_OF_FIRE );
  133. return( FALSE );
  134. }
  135. else
  136. {
  137. // Is this a different case?
  138. if ( gTacticalStatus.sCantGetThroughGridNo != sTargetGridNo || gTacticalStatus.ubCantGetThroughID != pSoldier->ubID || gTacticalStatus.sCantGetThroughSoldierGridNo != pSoldier->sGridNo )
  139. {
  140. // PLay quote
  141. gTacticalStatus.sCantGetThroughGridNo = sTargetGridNo;
  142. gTacticalStatus.ubCantGetThroughID = pSoldier->ubID;
  143. TacticalCharacterDialogue( pSoldier, QUOTE_NO_LINE_OF_FIRE );
  144. return( FALSE );
  145. }
  146. }
  147. }
  148. else
  149. {
  150. gTacticalStatus.fCantGetThrough = FALSE;
  151. }
  152. return( TRUE );
  153. }
  154. INT32 HandleItem( SOLDIERTYPE *pSoldier, UINT16 usGridNo, INT8 bLevel, UINT16 usHandItem, BOOLEAN fFromUI )
  155. {
  156. SOLDIERTYPE *pTargetSoldier = NULL;
  157. UINT16 usSoldierIndex;
  158. INT16 sTargetGridNo;
  159. INT16 sAPCost;
  160. INT16 sActionGridNo;
  161. UINT8 ubDirection;
  162. INT16 sAdjustedGridNo;
  163. BOOLEAN fDropBomb = FALSE;
  164. BOOLEAN fAddingTurningCost = FALSE;
  165. BOOLEAN fAddingRaiseGunCost = FALSE;
  166. LEVELNODE *pIntNode;
  167. STRUCTURE *pStructure;
  168. INT16 sGridNo;
  169. // Remove any previous actions
  170. pSoldier->ubPendingAction = NO_PENDING_ACTION;
  171. // here is where we would set a different value if the weapon mode is on
  172. // "attached weapon"
  173. pSoldier->usAttackingWeapon = usHandItem;
  174. // Find soldier flags depend on if it's our own merc firing or a NPC
  175. //if ( FindSoldier( usGridNo, &usSoldierIndex, &uiMercFlags, FIND_SOLDIER_GRIDNO ) )
  176. if ( ( usSoldierIndex = WhoIsThere2( usGridNo, bLevel ) ) != NO_SOLDIER )
  177. {
  178. pTargetSoldier = MercPtrs[ usSoldierIndex ];
  179. if ( fFromUI )
  180. {
  181. // ATE: Check if we are targeting an interactive tile, and adjust gridno accordingly...
  182. pIntNode = GetCurInteractiveTileGridNoAndStructure( &sGridNo, &pStructure );
  183. if ( pIntNode != NULL && pTargetSoldier == pSoldier )
  184. {
  185. // Truncate target sioldier
  186. pTargetSoldier = NULL;
  187. }
  188. }
  189. }
  190. // ATE: If in realtime, set attacker count to 0...
  191. if ( !(gTacticalStatus.uiFlags & INCOMBAT) )
  192. {
  193. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Setting attack busy count to 0 due to no combat" ) );
  194. gTacticalStatus.ubAttackBusyCount = 0;
  195. }
  196. if ( pTargetSoldier )
  197. {
  198. pTargetSoldier->bBeingAttackedCount = 0;
  199. }
  200. // Check our soldier's life for unconscious!
  201. if ( pSoldier->bLife < OKLIFE )
  202. {
  203. return( ITEM_HANDLE_UNCONSCIOUS );
  204. }
  205. if (HandItemWorks( pSoldier, HANDPOS ) == FALSE )
  206. {
  207. return( ITEM_HANDLE_BROKEN );
  208. }
  209. if ( fFromUI && pSoldier->bTeam == gbPlayerNum && pTargetSoldier && (pTargetSoldier->bTeam == gbPlayerNum || pTargetSoldier->bNeutral) && pTargetSoldier->ubBodyType != CROW && Item[ usHandItem ].usItemClass != IC_MEDKIT )
  210. {
  211. if ( pSoldier->ubProfile != NO_PROFILE )
  212. {
  213. // nice mercs won't shoot other nice guys or neutral civilians
  214. if ( (gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags3 & PROFILE_MISC_FLAG3_GOODGUY) && ( (pTargetSoldier->ubProfile == NO_PROFILE && pTargetSoldier->bNeutral) || gMercProfiles[ pTargetSoldier->ubProfile ].ubMiscFlags3 & PROFILE_MISC_FLAG3_GOODGUY) )
  215. {
  216. TacticalCharacterDialogue( pSoldier, QUOTE_REFUSING_ORDER );
  217. return( ITEM_HANDLE_REFUSAL );
  218. }
  219. if ( pTargetSoldier->ubProfile != NO_PROFILE )
  220. {
  221. // buddies won't shoot each other
  222. if ( WhichBuddy( pSoldier->ubProfile, pTargetSoldier->ubProfile ) != -1 )
  223. {
  224. TacticalCharacterDialogue( pSoldier, QUOTE_REFUSING_ORDER );
  225. return( ITEM_HANDLE_REFUSAL );
  226. }
  227. }
  228. // any recruited rebel will refuse to fire on another rebel or neutral nameless civ
  229. if ( pSoldier->ubCivilianGroup == REBEL_CIV_GROUP && (pTargetSoldier->ubCivilianGroup == REBEL_CIV_GROUP || ( pTargetSoldier->bNeutral && pTargetSoldier->ubProfile == NO_PROFILE && pTargetSoldier->ubCivilianGroup == NON_CIV_GROUP && pTargetSoldier->ubBodyType != CROW ) ) )
  230. {
  231. TacticalCharacterDialogue( pSoldier, QUOTE_REFUSING_ORDER );
  232. return( ITEM_HANDLE_REFUSAL );
  233. }
  234. }
  235. }
  236. // Check HAND ITEM
  237. if ( Item[ usHandItem ].usItemClass == IC_GUN || Item[ usHandItem ].usItemClass == IC_THROWING_KNIFE )
  238. {
  239. // WEAPONS
  240. if ( usHandItem == ROCKET_RIFLE || usHandItem == AUTO_ROCKET_RIFLE )
  241. {
  242. // check imprint ID
  243. // NB not-imprinted value is NO_PROFILE
  244. // imprinted value is profile for mercs & NPCs and NO_PROFILE + 1 for generic dudes
  245. if (pSoldier->ubProfile != NO_PROFILE)
  246. {
  247. if ( pSoldier->inv[ pSoldier->ubAttackingHand ].ubImprintID != pSoldier->ubProfile )
  248. {
  249. if ( pSoldier->inv[ pSoldier->ubAttackingHand ].ubImprintID == NO_PROFILE )
  250. {
  251. // first shot using "virgin" gun... set imprint ID
  252. pSoldier->inv[ pSoldier->ubAttackingHand ].ubImprintID = pSoldier->ubProfile;
  253. // this could be an NPC (Krott)
  254. if (pSoldier->bTeam == gbPlayerNum)
  255. {
  256. PlayJA2Sample( RG_ID_IMPRINTED, RATE_11025, HIGHVOLUME, 1, MIDDLE );
  257. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"\"%s\"", TacticalStr[ GUN_GOT_FINGERPRINT ] );
  258. return( ITEM_HANDLE_BROKEN );
  259. }
  260. }
  261. else
  262. {
  263. // access denied!
  264. if (pSoldier->bTeam == gbPlayerNum)
  265. {
  266. PlayJA2Sample( RG_ID_INVALID, RATE_11025, HIGHVOLUME, 1, MIDDLE );
  267. //if (Random( 100 ) < (UINT32) pSoldier->bWisdom)
  268. //{
  269. // DoMercBattleSound( pSoldier, BATTLE_SOUND_CURSE1 );
  270. //}
  271. //else
  272. //{
  273. // TacticalCharacterDialogue( pSoldier, QUOTE_USELESS_ITEM );
  274. //}
  275. }
  276. return( ITEM_HANDLE_BROKEN );
  277. }
  278. }
  279. }
  280. else
  281. {
  282. // guaranteed not to be controlled by the player, so no feedback required
  283. if ( pSoldier->inv[ pSoldier->ubAttackingHand ].ubImprintID != (NO_PROFILE + 1) )
  284. {
  285. if ( pSoldier->inv[ pSoldier->ubAttackingHand ].ubImprintID == NO_PROFILE )
  286. {
  287. pSoldier->inv[ pSoldier->ubAttackingHand ].ubImprintID = (NO_PROFILE + 1);
  288. }
  289. else
  290. {
  291. return( ITEM_HANDLE_BROKEN );
  292. }
  293. }
  294. }
  295. }
  296. // IF we are not a throwing knife, check for ammo, reloading...
  297. if ( Item[ usHandItem ].usItemClass != IC_THROWING_KNIFE )
  298. {
  299. // CHECK FOR AMMO!
  300. if ( !EnoughAmmo( pSoldier, fFromUI, HANDPOS ) )
  301. {
  302. //ATE: Reflect that we need to reset for bursting
  303. pSoldier->fDoSpread = FALSE;
  304. return( ITEM_HANDLE_NOAMMO );
  305. }
  306. // Check if we are reloading
  307. if ( (gTacticalStatus.uiFlags & REALTIME) || !(gTacticalStatus.uiFlags & INCOMBAT) )
  308. {
  309. if ( pSoldier->fReloading )
  310. {
  311. return( ITEM_HANDLE_RELOADING );
  312. }
  313. }
  314. }
  315. // Get gridno - either soldier's position or the gridno
  316. if ( pTargetSoldier != NULL )
  317. {
  318. sTargetGridNo = pTargetSoldier->sGridNo;
  319. }
  320. else
  321. {
  322. sTargetGridNo = usGridNo;
  323. }
  324. // If it's a player guy, check ChanceToGetThrough to play quote
  325. if ( fFromUI && (gTacticalStatus.uiFlags & TURNBASED ) && (gTacticalStatus.uiFlags & INCOMBAT) )
  326. {
  327. // Don't do if no spread!
  328. if ( !pSoldier->fDoSpread )
  329. {
  330. if ( !HandleCheckForBadChangeToGetThrough( pSoldier, pTargetSoldier, sTargetGridNo , bLevel ) )
  331. {
  332. return( ITEM_HANDLE_OK );
  333. }
  334. }
  335. }
  336. // Get AP COSTS
  337. // ATE: OK something funny going on here - AI seems to NEED FALSE here,
  338. // Our guys NEED TRUE. We shoulkd at some time make sure the AI and
  339. // our guys are deducting/checking in the same manner to avoid
  340. // these differences.
  341. sAPCost = CalcTotalAPsToAttack( pSoldier, sTargetGridNo, TRUE, pSoldier->bAimTime );
  342. GetAPChargeForShootOrStabWRTGunRaises( pSoldier, sTargetGridNo, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost );
  343. // If we are standing and are asked to turn AND raise gun, ignore raise gun...
  344. if ( gAnimControl[ pSoldier->usAnimState ].ubHeight == ANIM_STAND )
  345. {
  346. if ( fAddingRaiseGunCost )
  347. {
  348. pSoldier->fDontChargeTurningAPs = TRUE;
  349. }
  350. }
  351. else
  352. {
  353. // If raising gun, don't charge turning!
  354. if ( fAddingTurningCost )
  355. {
  356. pSoldier->fDontChargeReadyAPs = TRUE;
  357. }
  358. }
  359. // If this is a player guy, show message about no APS
  360. if ( EnoughPoints( pSoldier, sAPCost, 0, fFromUI ) )
  361. {
  362. if ( (pSoldier->ubProfile != NO_PROFILE) && (gMercProfiles[ pSoldier->ubProfile ].bPersonalityTrait == PSYCHO) )
  363. {
  364. // psychos might possibly switch to burst if they can
  365. if ( !pSoldier->bDoBurst && IsGunBurstCapable( pSoldier, HANDPOS, FALSE ) )
  366. {
  367. // chance of firing burst if we have points... chance decreasing when ordered to do aimed shot
  368. // temporarily set burst to true to calculate action points
  369. pSoldier->bDoBurst = TRUE;
  370. sAPCost = CalcTotalAPsToAttack( pSoldier, sTargetGridNo, TRUE, 0 );
  371. // reset burst mode to false (which is what it was at originally)
  372. pSoldier->bDoBurst = FALSE;
  373. if ( EnoughPoints( pSoldier, sAPCost, 0, FALSE ) )
  374. {
  375. // we have enough points to do this burst, roll the dice and see if we want to change
  376. if ( Random( 3 + pSoldier->bAimTime ) == 0 )
  377. {
  378. DoMercBattleSound( pSoldier, BATTLE_SOUND_LAUGH1 );
  379. pSoldier->bDoBurst = TRUE;
  380. pSoldier->bWeaponMode = WM_BURST;
  381. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzLateLocalizedString[ 26 ], pSoldier->name );
  382. }
  383. }
  384. }
  385. }
  386. // Deduct points if our target is different!
  387. // if attacking a new target (or if the specific target is uncertain)
  388. // DEF: Made into an event
  389. // EVENT_FireSoldierWeapon( pSoldier, sTargetGridNo );
  390. if (fFromUI)
  391. {
  392. // set the target level; if the AI calls this it will have set the level already...
  393. pSoldier->bTargetLevel = (INT8) gsInterfaceLevel;
  394. }
  395. if ( Item[ usHandItem ].usItemClass != IC_THROWING_KNIFE )
  396. {
  397. // If doing spread, set down the first gridno.....
  398. if ( pSoldier->fDoSpread )
  399. {
  400. if ( pSoldier->sSpreadLocations[ 0 ] != 0 )
  401. {
  402. SendBeginFireWeaponEvent( pSoldier, pSoldier->sSpreadLocations[ 0 ] );
  403. }
  404. else
  405. {
  406. SendBeginFireWeaponEvent( pSoldier, sTargetGridNo );
  407. }
  408. }
  409. else
  410. {
  411. SendBeginFireWeaponEvent( pSoldier, sTargetGridNo );
  412. }
  413. // ATE: Here to make cursor go back to move after LAW shot...
  414. if ( fFromUI && usHandItem == ROCKET_LAUNCHER )
  415. {
  416. guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
  417. }
  418. }
  419. else
  420. {
  421. UINT8 ubDirection;
  422. // Start knife throw attack
  423. // Get direction
  424. ubDirection = (UINT8)GetDirectionFromGridNo( sTargetGridNo, pSoldier );
  425. EVENT_SoldierBeginKnifeThrowAttack( pSoldier, sTargetGridNo, ubDirection );
  426. }
  427. if (fFromUI)
  428. {
  429. // Descrease aim by two if in real time
  430. if ( (gTacticalStatus.uiFlags & REALTIME ) || !(gTacticalStatus.uiFlags & INCOMBAT) )
  431. {
  432. //pSoldier->bShownAimTime -= 2;
  433. //if ( pSoldier->bShownAimTime < REFINE_AIM_1 )
  434. //{
  435. // pSoldier->bShownAimTime = REFINE_AIM_1;
  436. //}
  437. //pSoldier->fPauseAim = TRUE;
  438. }
  439. // If in turn based - refresh aim to first level
  440. if ( gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT) )
  441. {
  442. pSoldier->bShownAimTime = REFINE_AIM_1;
  443. // Locate to soldier if he's about to shoot!
  444. if ( pSoldier->bTeam != gbPlayerNum )
  445. {
  446. ShowRadioLocator( pSoldier->ubID, SHOW_LOCATOR_NORMAL );
  447. }
  448. }
  449. }
  450. // OK, set UI
  451. SetUIBusy( pSoldier->ubID );
  452. }
  453. else
  454. {
  455. return( ITEM_HANDLE_NOAPS );
  456. }
  457. return( ITEM_HANDLE_OK );
  458. }
  459. //TRY PUNCHING
  460. if ( Item[ usHandItem ].usItemClass == IC_PUNCH )
  461. {
  462. INT16 sCnt;
  463. INT16 sSpot;
  464. UINT8 ubGuyThere;
  465. INT16 sGotLocation = NOWHERE;
  466. BOOLEAN fGotAdjacent = FALSE;
  467. for ( sCnt = 0; sCnt < NUM_WORLD_DIRECTIONS; sCnt++ )
  468. {
  469. sSpot = NewGridNo( pSoldier->sGridNo, DirectionInc( sCnt ) );
  470. // Make sure movement costs are OK....
  471. if ( gubWorldMovementCosts[ sSpot ][ sCnt ][ bLevel ] >= TRAVELCOST_BLOCKED )
  472. {
  473. continue;
  474. }
  475. // Check for who is there...
  476. ubGuyThere = WhoIsThere2( sSpot, pSoldier->bLevel );
  477. if ( pTargetSoldier != NULL && ubGuyThere == pTargetSoldier->ubID )
  478. {
  479. // We've got a guy here....
  480. // Who is the one we want......
  481. sGotLocation = sSpot;
  482. sAdjustedGridNo = pTargetSoldier->sGridNo;
  483. ubDirection = ( UINT8 )sCnt;
  484. break;
  485. }
  486. }
  487. if ( sGotLocation == NOWHERE )
  488. {
  489. // See if we can get there to punch
  490. sActionGridNo = FindAdjacentGridEx( pSoldier, usGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
  491. if ( sActionGridNo != -1 )
  492. {
  493. // OK, we've got somebody...
  494. sGotLocation = sActionGridNo;
  495. fGotAdjacent = TRUE;
  496. }
  497. }
  498. // Did we get a loaction?
  499. if ( sGotLocation != NOWHERE )
  500. {
  501. pSoldier->sTargetGridNo = usGridNo;
  502. pSoldier->usActionData = usGridNo;
  503. // CHECK IF WE ARE AT THIS GRIDNO NOW
  504. if ( pSoldier->sGridNo != sGotLocation && fGotAdjacent )
  505. {
  506. // SEND PENDING ACTION
  507. pSoldier->ubPendingAction = MERC_PUNCH;
  508. pSoldier->sPendingActionData2 = sAdjustedGridNo;
  509. pSoldier->bPendingActionData3 = ubDirection;
  510. pSoldier->ubPendingActionAnimCount = 0;
  511. // WALK UP TO DEST FIRST
  512. EVENT_InternalGetNewSoldierPath( pSoldier, sGotLocation, pSoldier->usUIMovementMode, FALSE, TRUE );
  513. }
  514. else
  515. {
  516. pSoldier->bAction = AI_ACTION_KNIFE_STAB;
  517. EVENT_SoldierBeginPunchAttack( pSoldier, sAdjustedGridNo, ubDirection );
  518. }
  519. // OK, set UI
  520. SetUIBusy( pSoldier->ubID );
  521. gfResetUIMovementOptimization = TRUE;
  522. return( ITEM_HANDLE_OK );
  523. }
  524. else
  525. {
  526. return( ITEM_HANDLE_CANNOT_GETTO_LOCATION );
  527. }
  528. }
  529. //USING THE MEDKIT
  530. if ( Item[ usHandItem ].usItemClass == IC_MEDKIT )
  531. {
  532. // ATE: AI CANNOT GO THROUGH HERE!
  533. UINT16 usMapPos;
  534. BOOLEAN fHadToUseCursorPos = FALSE;
  535. if (gTacticalStatus.fAutoBandageMode)
  536. {
  537. usMapPos = usGridNo;
  538. }
  539. else
  540. {
  541. GetMouseMapPos( &usMapPos );
  542. }
  543. // See if we can get there to stab
  544. sActionGridNo = FindAdjacentGridEx( pSoldier, usGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
  545. if ( sActionGridNo == -1 )
  546. {
  547. // Try another location...
  548. sActionGridNo = FindAdjacentGridEx( pSoldier, usMapPos, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
  549. if ( sActionGridNo == -1 )
  550. {
  551. return( ITEM_HANDLE_CANNOT_GETTO_LOCATION );
  552. }
  553. if ( !gTacticalStatus.fAutoBandageMode )
  554. {
  555. fHadToUseCursorPos = TRUE;
  556. }
  557. }
  558. // Calculate AP costs...
  559. sAPCost = GetAPsToBeginFirstAid( pSoldier );
  560. sAPCost += PlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, FALSE, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints);
  561. if ( EnoughPoints( pSoldier, sAPCost, 0, fFromUI ) )
  562. {
  563. // OK, set UI
  564. SetUIBusy( pSoldier->ubID );
  565. // CHECK IF WE ARE AT THIS GRIDNO NOW
  566. if ( pSoldier->sGridNo != sActionGridNo )
  567. {
  568. // SEND PENDING ACTION
  569. pSoldier->ubPendingAction = MERC_GIVEAID;
  570. if ( fHadToUseCursorPos )
  571. {
  572. pSoldier->sPendingActionData2 = usMapPos;
  573. }
  574. else
  575. {
  576. if ( pTargetSoldier != NULL )
  577. {
  578. pSoldier->sPendingActionData2 = pTargetSoldier->sGridNo;
  579. }
  580. else
  581. {
  582. pSoldier->sPendingActionData2 = usGridNo;
  583. }
  584. }
  585. pSoldier->bPendingActionData3 = ubDirection;
  586. pSoldier->ubPendingActionAnimCount = 0;
  587. // WALK UP TO DEST FIRST
  588. EVENT_InternalGetNewSoldierPath( pSoldier, sActionGridNo, pSoldier->usUIMovementMode, FALSE, TRUE );
  589. }
  590. else
  591. {
  592. EVENT_SoldierBeginFirstAid( pSoldier, sAdjustedGridNo, ubDirection );
  593. }
  594. if ( fFromUI )
  595. {
  596. guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
  597. }
  598. return( ITEM_HANDLE_OK );
  599. }
  600. else
  601. {
  602. return( ITEM_HANDLE_NOAPS );
  603. }
  604. }
  605. if ( usHandItem == WIRECUTTERS )
  606. {
  607. // See if we can get there to stab
  608. sActionGridNo = FindAdjacentGridEx( pSoldier, usGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
  609. if ( sActionGridNo != -1 )
  610. {
  611. // Calculate AP costs...
  612. sAPCost = GetAPsToCutFence( pSoldier );
  613. sAPCost += PlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, FALSE, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints);
  614. if ( EnoughPoints( pSoldier, sAPCost, 0, fFromUI ) )
  615. {
  616. // CHECK IF WE ARE AT THIS GRIDNO NOW
  617. if ( pSoldier->sGridNo != sActionGridNo )
  618. {
  619. // SEND PENDING ACTION
  620. pSoldier->ubPendingAction = MERC_CUTFFENCE;
  621. pSoldier->sPendingActionData2 = sAdjustedGridNo;
  622. pSoldier->bPendingActionData3 = ubDirection;
  623. pSoldier->ubPendingActionAnimCount = 0;
  624. // WALK UP TO DEST FIRST
  625. EVENT_InternalGetNewSoldierPath( pSoldier, sActionGridNo, pSoldier->usUIMovementMode , FALSE, TRUE );
  626. }
  627. else
  628. {
  629. EVENT_SoldierBeginCutFence( pSoldier, sAdjustedGridNo, ubDirection );
  630. }
  631. // OK, set UI
  632. SetUIBusy( pSoldier->ubID );
  633. if ( fFromUI )
  634. {
  635. guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
  636. }
  637. return( ITEM_HANDLE_OK );
  638. }
  639. else
  640. {
  641. return( ITEM_HANDLE_NOAPS );
  642. }
  643. }
  644. else
  645. {
  646. return( ITEM_HANDLE_CANNOT_GETTO_LOCATION );
  647. }
  648. }
  649. if ( usHandItem == TOOLKIT )
  650. {
  651. UINT8 ubMercID;
  652. BOOLEAN fVehicle = FALSE;
  653. INT16 sVehicleGridNo=-1;
  654. // For repair, check if we are over a vehicle, then get gridnot to edge of that vehicle!
  655. if ( IsRepairableStructAtGridNo( usGridNo, &ubMercID ) == 2 )
  656. {
  657. INT16 sNewGridNo;
  658. UINT8 ubDirection;
  659. sNewGridNo = FindGridNoFromSweetSpotWithStructDataFromSoldier( pSoldier, pSoldier->usUIMovementMode, 5, &ubDirection, 0, MercPtrs[ ubMercID ] );
  660. if ( sNewGridNo != NOWHERE )
  661. {
  662. usGridNo = sNewGridNo;
  663. sVehicleGridNo = MercPtrs[ ubMercID ]->sGridNo;
  664. fVehicle = TRUE;
  665. }
  666. }
  667. // See if we can get there to stab
  668. sActionGridNo = FindAdjacentGridEx( pSoldier, usGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
  669. if ( sActionGridNo != -1 )
  670. {
  671. // Calculate AP costs...
  672. sAPCost = GetAPsToBeginRepair( pSoldier );
  673. sAPCost += PlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, FALSE, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints);
  674. if ( EnoughPoints( pSoldier, sAPCost, 0, fFromUI ) )
  675. {
  676. // CHECK IF WE ARE AT THIS GRIDNO NOW
  677. if ( pSoldier->sGridNo != sActionGridNo )
  678. {
  679. // SEND PENDING ACTION
  680. pSoldier->ubPendingAction = MERC_REPAIR;
  681. pSoldier->sPendingActionData2 = sAdjustedGridNo;
  682. if ( fVehicle )
  683. {
  684. pSoldier->sPendingActionData2 = sVehicleGridNo;
  685. }
  686. pSoldier->bPendingActionData3 = ubDirection;
  687. pSoldier->ubPendingActionAnimCount = 0;
  688. // WALK UP TO DEST FIRST
  689. EVENT_InternalGetNewSoldierPath( pSoldier, sActionGridNo, pSoldier->usUIMovementMode, FALSE, TRUE );
  690. }
  691. else
  692. {
  693. EVENT_SoldierBeginRepair( pSoldier, sAdjustedGridNo, ubDirection );
  694. }
  695. // OK, set UI
  696. SetUIBusy( pSoldier->ubID );
  697. if ( fFromUI )
  698. {
  699. guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
  700. }
  701. return( ITEM_HANDLE_OK );
  702. }
  703. else
  704. {
  705. return( ITEM_HANDLE_NOAPS );
  706. }
  707. }
  708. else
  709. {
  710. return( ITEM_HANDLE_CANNOT_GETTO_LOCATION );
  711. }
  712. }
  713. if ( usHandItem == GAS_CAN )
  714. {
  715. UINT8 ubMercID;
  716. INT16 sVehicleGridNo=-1;
  717. // For repair, check if we are over a vehicle, then get gridnot to edge of that vehicle!
  718. if ( IsRefuelableStructAtGridNo( usGridNo, &ubMercID ) )
  719. {
  720. INT16 sNewGridNo;
  721. UINT8 ubDirection;
  722. sNewGridNo = FindGridNoFromSweetSpotWithStructDataFromSoldier( pSoldier, pSoldier->usUIMovementMode, 5, &ubDirection, 0, MercPtrs[ ubMercID ] );
  723. if ( sNewGridNo != NOWHERE )
  724. {
  725. usGridNo = sNewGridNo;
  726. sVehicleGridNo = MercPtrs[ ubMercID ]->sGridNo;
  727. }
  728. }
  729. // See if we can get there to stab
  730. sActionGridNo = FindAdjacentGridEx( pSoldier, usGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
  731. if ( sActionGridNo != -1 )
  732. {
  733. // Calculate AP costs...
  734. sAPCost = GetAPsToRefuelVehicle( pSoldier );
  735. sAPCost += PlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, FALSE, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints);
  736. if ( EnoughPoints( pSoldier, sAPCost, 0, fFromUI ) )
  737. {
  738. // CHECK IF WE ARE AT THIS GRIDNO NOW
  739. if ( pSoldier->sGridNo != sActionGridNo )
  740. {
  741. // SEND PENDING ACTION
  742. pSoldier->ubPendingAction = MERC_FUEL_VEHICLE;
  743. pSoldier->sPendingActionData2 = sAdjustedGridNo;
  744. pSoldier->sPendingActionData2 = sVehicleGridNo;
  745. pSoldier->bPendingActionData3 = ubDirection;
  746. pSoldier->ubPendingActionAnimCount = 0;
  747. // WALK UP TO DEST FIRST
  748. EVENT_InternalGetNewSoldierPath( pSoldier, sActionGridNo, pSoldier->usUIMovementMode, FALSE, TRUE );
  749. }
  750. else
  751. {
  752. EVENT_SoldierBeginRefuel( pSoldier, sAdjustedGridNo, ubDirection );
  753. }
  754. // OK, set UI
  755. SetUIBusy( pSoldier->ubID );
  756. if ( fFromUI )
  757. {
  758. guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
  759. }
  760. return( ITEM_HANDLE_OK );
  761. }
  762. else
  763. {
  764. return( ITEM_HANDLE_NOAPS );
  765. }
  766. }
  767. else
  768. {
  769. return( ITEM_HANDLE_CANNOT_GETTO_LOCATION );
  770. }
  771. }
  772. if ( usHandItem == JAR )
  773. {
  774. sActionGridNo = FindAdjacentGridEx( pSoldier, usGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
  775. if ( sActionGridNo != -1 )
  776. {
  777. // Calculate AP costs...
  778. sAPCost = GetAPsToUseJar( pSoldier, sActionGridNo );
  779. sAPCost += PlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, FALSE, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints);
  780. if ( EnoughPoints( pSoldier, sAPCost, 0, fFromUI ) )
  781. {
  782. // CHECK IF WE ARE AT THIS GRIDNO NOW
  783. if ( pSoldier->sGridNo != sActionGridNo )
  784. {
  785. // SEND PENDING ACTION
  786. pSoldier->ubPendingAction = MERC_TAKEBLOOD;
  787. pSoldier->sPendingActionData2 = sAdjustedGridNo;
  788. pSoldier->bPendingActionData3 = ubDirection;
  789. pSoldier->ubPendingActionAnimCount = 0;
  790. // WALK UP TO DEST FIRST
  791. EVENT_InternalGetNewSoldierPath( pSoldier, sActionGridNo, pSoldier->usUIMovementMode, FALSE, TRUE );
  792. }
  793. else
  794. {
  795. EVENT_SoldierBeginTakeBlood( pSoldier, sAdjustedGridNo, ubDirection );
  796. }
  797. // OK, set UI
  798. SetUIBusy( pSoldier->ubID );
  799. if ( fFromUI )
  800. {
  801. guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
  802. }
  803. return( ITEM_HANDLE_OK );
  804. }
  805. else
  806. {
  807. return( ITEM_HANDLE_NOAPS );
  808. }
  809. }
  810. else
  811. {
  812. return( ITEM_HANDLE_CANNOT_GETTO_LOCATION );
  813. }
  814. }
  815. if ( usHandItem == STRING_TIED_TO_TIN_CAN )
  816. {
  817. STRUCTURE *pStructure;
  818. LEVELNODE *pIntTile;
  819. // Get structure info for in tile!
  820. pIntTile = GetCurInteractiveTileGridNoAndStructure( &usGridNo, &pStructure );
  821. // We should not have null here if we are given this flag...
  822. if ( pIntTile != NULL )
  823. {
  824. sActionGridNo = FindAdjacentGridEx( pSoldier, usGridNo, &ubDirection, &sAdjustedGridNo, FALSE, TRUE );
  825. if ( sActionGridNo != -1 )
  826. {
  827. // Calculate AP costs...
  828. sAPCost = AP_ATTACH_CAN;
  829. sAPCost += PlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, FALSE, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints);
  830. if ( EnoughPoints( pSoldier, sAPCost, 0, fFromUI ) )
  831. {
  832. // CHECK IF WE ARE AT THIS GRIDNO NOW
  833. if ( pSoldier->sGridNo != sActionGridNo )
  834. {
  835. // SEND PENDING ACTION
  836. pSoldier->ubPendingAction = MERC_ATTACH_CAN;
  837. pSoldier->sPendingActionData2 = usGridNo;
  838. pSoldier->bPendingActionData3 = ubDirection;
  839. pSoldier->ubPendingActionAnimCount = 0;
  840. // WALK UP TO DEST FIRST
  841. EVENT_InternalGetNewSoldierPath( pSoldier, sActionGridNo, pSoldier->usUIMovementMode, FALSE, TRUE );
  842. }
  843. else
  844. {
  845. EVENT_SoldierBeginTakeBlood( pSoldier, usGridNo, ubDirection );
  846. }
  847. // OK, set UI
  848. SetUIBusy( pSoldier->ubID );
  849. if ( fFromUI )
  850. {
  851. guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
  852. }
  853. return( ITEM_HANDLE_OK );
  854. }
  855. else
  856. {
  857. return( ITEM_HANDLE_NOAPS );
  858. }
  859. }
  860. else
  861. {
  862. return( ITEM_HANDLE_CANNOT_GETTO_LOCATION );
  863. }
  864. }
  865. else
  866. {
  867. return( ITEM_HANDLE_CANNOT_GETTO_LOCATION );
  868. }
  869. }
  870. // Check for remote detonator cursor....
  871. if ( Item[ usHandItem ].ubCursor == REMOTECURS )
  872. {
  873. sAPCost = AP_USE_REMOTE;
  874. if ( EnoughPoints( pSoldier, sAPCost, 0, fFromUI ) )
  875. {
  876. DeductPoints( pSoldier, sAPCost, 0 );
  877. if ( usHandItem == XRAY_DEVICE )
  878. {
  879. PlayJA2Sample( USE_X_RAY_MACHINE, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
  880. ActivateXRayDevice( pSoldier );
  881. return( ITEM_HANDLE_OK );
  882. }
  883. else // detonator
  884. {
  885. // Save gridno....
  886. pSoldier->sPendingActionData2 = usGridNo;
  887. EVENT_SoldierBeginUseDetonator( pSoldier );
  888. if ( fFromUI )
  889. {
  890. guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
  891. }
  892. // Now start anim....
  893. return( ITEM_HANDLE_OK );
  894. }
  895. }
  896. else
  897. {
  898. return( ITEM_HANDLE_NOAPS );
  899. }
  900. }
  901. // Check for mine.. anything without a detonator.....
  902. if ( Item[ usHandItem ].ubCursor == BOMBCURS )
  903. {
  904. fDropBomb = TRUE;
  905. }
  906. // Check for a bomb like a mine, that uses a pressure detonator
  907. if ( Item[ usHandItem ].ubCursor == INVALIDCURS )
  908. {
  909. // Found detonator...
  910. if ( FindAttachment( &(pSoldier->inv[ pSoldier->ubAttackingHand ] ), DETONATOR) != ITEM_NOT_FOUND || FindAttachment( &(pSoldier->inv[ pSoldier->ubAttackingHand ] ), REMDETONATOR ) != ITEM_NOT_FOUND )
  911. {
  912. fDropBomb = TRUE;
  913. }
  914. }
  915. if ( fDropBomb )
  916. {
  917. // Save gridno....
  918. pSoldier->sPendingActionData2 = usGridNo;
  919. if ( pSoldier->sGridNo != usGridNo )
  920. {
  921. // SEND PENDING ACTION
  922. pSoldier->ubPendingAction = MERC_DROPBOMB;
  923. pSoldier->ubPendingActionAnimCount = 0;
  924. // WALK UP TO DEST FIRST
  925. EVENT_InternalGetNewSoldierPath( pSoldier, usGridNo, pSoldier->usUIMovementMode, FALSE, TRUE );
  926. }
  927. else
  928. {
  929. EVENT_SoldierBeginDropBomb( pSoldier );
  930. }
  931. // OK, set UI
  932. SetUIBusy( pSoldier->ubID );
  933. if ( fFromUI )
  934. {
  935. guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
  936. }
  937. return( ITEM_HANDLE_OK );
  938. }
  939. //USING THE BLADE
  940. if ( Item[ usHandItem ].usItemClass == IC_BLADE )
  941. {
  942. // See if we can get there to stab
  943. if ( pSoldier->ubBodyType == BLOODCAT )
  944. {
  945. sActionGridNo = FindNextToAdjacentGridEx( pSoldier, usGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
  946. }
  947. else if ( CREATURE_OR_BLOODCAT( pSoldier ) && PythSpacesAway( pSoldier->sGridNo, usGridNo ) > 1 )
  948. {
  949. sActionGridNo = FindNextToAdjacentGridEx( pSoldier, usGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
  950. if (sActionGridNo == -1)
  951. {
  952. sActionGridNo = FindAdjacentGridEx( pSoldier, usGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
  953. }
  954. }
  955. else
  956. {
  957. sActionGridNo = FindAdjacentGridEx( pSoldier, usGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
  958. }
  959. if ( sActionGridNo != -1 )
  960. {
  961. pSoldier->usActionData = sActionGridNo;
  962. // CHECK IF WE ARE AT THIS GRIDNO NOW
  963. if ( pSoldier->sGridNo != sActionGridNo )
  964. {
  965. // SEND PENDING ACTION
  966. pSoldier->ubPendingAction = MERC_KNIFEATTACK;
  967. pSoldier->sPendingActionData2 = sAdjustedGridNo;
  968. pSoldier->bPendingActionData3 = ubDirection;
  969. pSoldier->ubPendingActionAnimCount = 0;
  970. // WALK UP TO DEST FIRST
  971. EVENT_InternalGetNewSoldierPath( pSoldier, sActionGridNo, pSoldier->usUIMovementMode, FALSE, TRUE );
  972. }
  973. else
  974. {
  975. // for the benefit of the AI
  976. pSoldier->bAction = AI_ACTION_KNIFE_STAB;
  977. EVENT_SoldierBeginBladeAttack( pSoldier, sAdjustedGridNo, ubDirection );
  978. }
  979. // OK, set UI
  980. SetUIBusy( pSoldier->ubID );
  981. if ( fFromUI )
  982. {
  983. guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
  984. gfResetUIMovementOptimization = TRUE;
  985. }
  986. return( ITEM_HANDLE_OK );
  987. }
  988. else
  989. {
  990. return( ITEM_HANDLE_CANNOT_GETTO_LOCATION );
  991. }
  992. }
  993. if ( Item[ usHandItem ].usItemClass == IC_TENTACLES )
  994. {
  995. // See if we can get there to stab
  996. //pSoldier->sTargetGridNo = sTargetGridNo;
  997. //pSoldier->sLastTarget = sTargetGridNo;
  998. //pSoldier->ubTargetID = WhoIsThere2( sTargetGridNo, pSoldier->bTargetLevel );
  999. gTacticalStatus.ubAttackBusyCount++;
  1000. DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting swipe attack, incrementing a.b.c in HandleItems to %d", gTacticalStatus.ubAttackBusyCount) );
  1001. sAPCost = CalcTotalAPsToAttack( pSoldier, sGridNo, FALSE, pSoldier->bAimTime );
  1002. DeductPoints( pSoldier, sAPCost, 0 );
  1003. EVENT_InitNewSoldierAnim( pSoldier, QUEEN_SWIPE, 0 , FALSE );
  1004. //FireWeapon( pSoldier, sTargetGridNo );
  1005. pSoldier->bAction = AI_ACTION_KNIFE_STAB;
  1006. return( ITEM_HANDLE_OK );
  1007. }
  1008. // THIS IS IF WE WERE FROM THE UI
  1009. if ( Item[ usHandItem ].usItemClass == IC_GRENADE || Item[ usHandItem ].usItemClass == IC_LAUNCHER || Item[ usHandItem ].usItemClass == IC_THROWN )
  1010. {
  1011. INT16 sCheckGridNo;
  1012. // Get gridno - either soldier's position or the gridno
  1013. if ( pTargetSoldier != NULL )
  1014. {
  1015. sTargetGridNo = pTargetSoldier->sGridNo;
  1016. }
  1017. else
  1018. {
  1019. sTargetGridNo = usGridNo;
  1020. }
  1021. sAPCost = MinAPsToAttack( pSoldier, sTargetGridNo, TRUE );
  1022. // Check if these is room to place mortar!
  1023. if ( usHandItem == MORTAR )
  1024. {
  1025. ubDirection = (UINT8)GetDirectionFromGridNo( sTargetGridNo, pSoldier );
  1026. // Get new gridno!
  1027. sCheckGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc( ubDirection ) );
  1028. if ( !OKFallDirection( pSoldier, sCheckGridNo, pSoldier->bLevel, ubDirection , pSoldier->usAnimState ) )
  1029. {
  1030. return( ITEM_HANDLE_NOROOM );
  1031. }
  1032. pSoldier->fDontChargeAPsForStanceChange = TRUE;
  1033. }
  1034. else if ( usHandItem == GLAUNCHER || usHandItem == UNDER_GLAUNCHER )
  1035. {
  1036. GetAPChargeForShootOrStabWRTGunRaises( pSoldier, sTargetGridNo, TRUE, &fAddingTurningCost, &fAddingRaiseGunCost );
  1037. // If we are standing and are asked to turn AND raise gun, ignore raise gun...
  1038. if ( gAnimControl[ pSoldier->usAnimState ].ubHeight == ANIM_STAND )
  1039. {
  1040. if ( fAddingRaiseGunCost )
  1041. {
  1042. pSoldier->fDontChargeTurningAPs = TRUE;
  1043. }
  1044. }
  1045. else
  1046. {
  1047. // If raising gun, don't charge turning!
  1048. if ( fAddingTurningCost )
  1049. {
  1050. pSoldier->fDontChargeReadyAPs = TRUE;
  1051. }
  1052. }
  1053. }
  1054. // If this is a player guy, show message about no APS
  1055. if ( EnoughPoints( pSoldier, sAPCost, 0, fFromUI ) )
  1056. {
  1057. pSoldier->ubAttackingHand = HANDPOS;
  1058. pSoldier->usAttackingWeapon = usHandItem;
  1059. pSoldier->bTargetLevel = bLevel;
  1060. // Look at the cursor, if toss cursor...
  1061. if ( Item[ usHandItem ].ubCursor == TOSSCURS )
  1062. {
  1063. pSoldier->sTargetGridNo = sTargetGridNo;
  1064. // pSoldier->sLastTarget = sTargetGridNo;
  1065. pSoldier->ubTargetID = WhoIsThere2( sTargetGridNo, pSoldier->bTargetLevel );
  1066. // Increment attack counter...
  1067. gTacticalStatus.ubAttackBusyCount++;
  1068. // ATE: Don't charge turning...
  1069. pSoldier->fDontChargeTurningAPs = TRUE;
  1070. FireWeapon( pSoldier, sTargetGridNo );
  1071. }
  1072. else
  1073. {
  1074. SendBeginFireWeaponEvent( pSoldier, sTargetGridNo );
  1075. }
  1076. // OK, set UI
  1077. SetUIBusy( pSoldier->ubID );
  1078. return( ITEM_HANDLE_OK );
  1079. }
  1080. else
  1081. {
  1082. return( ITEM_HANDLE_NOAPS );
  1083. }
  1084. return( ITEM_HANDLE_OK );
  1085. }
  1086. // CHECK FOR BOMB....
  1087. if ( Item[ usHandItem ].ubCursor == INVALIDCURS )
  1088. {
  1089. // Found detonator...
  1090. if ( FindAttachment( &(pSoldier->inv[ usHandItem ] ), DETONATOR) != ITEM_NOT_FOUND || FindAttachment( &(pSoldier->inv[ usHandItem ] ), REMDETONATOR ) )
  1091. {
  1092. StartBombMessageBox( pSoldier, usGridNo );
  1093. if ( fFromUI )
  1094. {
  1095. guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
  1096. }
  1097. return( ITEM_HANDLE_OK );
  1098. }
  1099. }
  1100. return( ITEM_HANDLE_OK );
  1101. }
  1102. void HandleSoldierDropBomb( SOLDIERTYPE *pSoldier, INT16 sGridNo )
  1103. {
  1104. // Does this have detonator that needs info?
  1105. if ( FindAttachment( &(pSoldier->inv[ HANDPOS ] ), DETONATOR) != ITEM_NOT_FOUND || FindAttachment( &(pSoldier->inv[ HANDPOS ] ), REMDETONATOR ) != ITEM_NOT_FOUND )
  1106. {
  1107. StartBombMessageBox( pSoldier, sGridNo );
  1108. }
  1109. else
  1110. {
  1111. // We have something... all we do is place...
  1112. if ( ArmBomb( &(pSoldier->inv[ HANDPOS ]), 0 ) )
  1113. {
  1114. // EXPLOSIVES GAIN (25): Place a bomb, or buried and armed a mine
  1115. StatChange( pSoldier, EXPLODEAMT, 25, FALSE );
  1116. pSoldier->inv[ HANDPOS ].bTrap = __min( 10, ( EffectiveExplosive( pSoldier ) / 20) + (EffectiveExpLevel( pSoldier ) / 3) );
  1117. pSoldier->inv[ HANDPOS ].ubBombOwner = pSoldier->ubID + 2;
  1118. // we now know there is something nasty here
  1119. gpWorldLevelData[ sGridNo ].uiFlags |= MAPELEMENT_PLAYER_MINE_PRESENT;
  1120. AddItemToPool( sGridNo, &(pSoldier->inv[ HANDPOS ] ), BURIED, pSoldier->bLevel, WORLD_ITEM_ARMED_BOMB, 0 );
  1121. DeleteObj( &(pSoldier->inv[ HANDPOS ]) );
  1122. }
  1123. }
  1124. }
  1125. void HandleSoldierUseRemote( SOLDIERTYPE *pSoldier, INT16 sGridNo )
  1126. {
  1127. StartBombMessageBox( pSoldier, sGridNo );
  1128. }
  1129. void SoldierHandleDropItem( SOLDIERTYPE *pSoldier )
  1130. {
  1131. // LOOK IN PANDING DATA FOR ITEM TO DROP, AND LOCATION
  1132. if ( pSoldier->pTempObject != NULL )
  1133. {
  1134. if ( pSoldier->bVisible != -1 )
  1135. {
  1136. PlayJA2Sample( THROW_IMPACT_2, RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
  1137. }
  1138. AddItemToPool( pSoldier->sGridNo, pSoldier->pTempObject, 1, pSoldier->bLevel, 0 , -1 );
  1139. NotifySoldiersToLookforItems( );
  1140. MemFree( pSoldier->pTempObject );
  1141. pSoldier->pTempObject = NULL;
  1142. }
  1143. }
  1144. void HandleSoldierThrowItem( SOLDIERTYPE *pSoldier, INT16 sGridNo )
  1145. {
  1146. // Determine what to do
  1147. UINT8 ubDirection;
  1148. // Set attacker to NOBODY, since it's not a combat attack
  1149. pSoldier->ubTargetID = NOBODY;
  1150. // Alrighty, switch based on stance!
  1151. switch( gAnimControl[ pSoldier->usAnimState ].ubHeight )
  1152. {
  1153. case ANIM_STAND:
  1154. // CHECK IF WE ARE NOT ON THE SAME GRIDNO
  1155. if ( sGridNo == pSoldier->sGridNo )
  1156. {
  1157. PickDropItemAnimation( pSoldier );
  1158. }
  1159. else
  1160. {
  1161. // CHANGE DIRECTION AT LEAST
  1162. ubDirection = (UINT8)GetDirectionFromGridNo( sGridNo, pSoldier );
  1163. SoldierGotoStationaryStance( pSoldier );
  1164. EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
  1165. pSoldier->fTurningUntilDone = TRUE;
  1166. // Draw item depending on distance from buddy
  1167. if ( GetRangeFromGridNoDiff( sGridNo, pSoldier->sGridNo ) < MIN_LOB_RANGE )
  1168. {
  1169. pSoldier->usPendingAnimation = LOB_ITEM;
  1170. }
  1171. else
  1172. {
  1173. pSoldier->usPendingAnimation = THROW_ITEM;
  1174. }
  1175. }
  1176. break;
  1177. case ANIM_CROUCH:
  1178. case ANIM_PRONE:
  1179. // CHECK IF WE ARE NOT ON THE SAME GRIDNO
  1180. if ( sGridNo == pSoldier->sGridNo )
  1181. {
  1182. // OK, JUST DROP ITEM!
  1183. if ( pSoldier->pTempObject != NULL )
  1184. {
  1185. AddItemToPool( sGridNo, pSoldier->pTempObject, 1, pSoldier->bLevel, 0, -1 );
  1186. NotifySoldiersToLookforItems( );
  1187. MemFree( pSoldier->pTempObject );
  1188. pSoldier->pTempObject = NULL;
  1189. }
  1190. }
  1191. else
  1192. {
  1193. // OK, go from prone/crouch to stand first!
  1194. ubDirection = (UINT8)GetDirectionFromGridNo( sGridNo, pSoldier );
  1195. EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
  1196. ChangeSoldierState( pSoldier, THROW_ITEM, 0 , FALSE );
  1197. }
  1198. }
  1199. }
  1200. void SoldierGiveItem( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTargetSoldier, OBJECTTYPE *pObject, INT8 bInvPos )
  1201. {
  1202. INT16 sActionGridNo, sAdjustedGridNo;
  1203. UINT8 ubDirection;
  1204. // Remove any previous actions
  1205. pSoldier->ubPendingAction = NO_PENDING_ACTION;
  1206. // See if we can get there to stab
  1207. sActionGridNo = FindAdjacentGridEx( pSoldier, pTargetSoldier->sGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
  1208. if ( sActionGridNo != -1 )
  1209. {
  1210. // SEND PENDING ACTION
  1211. pSoldier->ubPendingAction = MERC_GIVEITEM;
  1212. pSoldier->bPendingActionData5 = bInvPos;
  1213. // Copy temp object
  1214. pSoldier->pTempObject = MemAlloc( sizeof( OBJECTTYPE ) );
  1215. memcpy( pSoldier->pTempObject, pObject, sizeof( OBJECTTYPE ) );
  1216. pSoldier->sPendingActionData2 = pTargetSoldier->sGridNo;
  1217. pSoldier->bPendingActionData3 = ubDirection;
  1218. pSoldier->uiPendingActionData4 = pTargetSoldier->ubID;
  1219. pSoldier->ubPendingActionAnimCount = 0;
  1220. // Set soldier as engaged!
  1221. pSoldier->uiStatusFlags |= SOLDIER_ENGAGEDINACTION;
  1222. // CHECK IF WE ARE AT THIS GRIDNO NOW
  1223. if ( pSoldier->sGridNo != sActionGridNo )
  1224. {
  1225. // WALK UP TO DEST FIRST
  1226. EVENT_InternalGetNewSoldierPath( pSoldier, sActionGridNo, pSoldier->usUIMovementMode, FALSE, TRUE );
  1227. }
  1228. else
  1229. {
  1230. EVENT_SoldierBeginGiveItem( pSoldier );
  1231. // CHANGE DIRECTION OF TARGET TO OPPOSIDE DIRECTION!
  1232. EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
  1233. }
  1234. // Set target as engaged!
  1235. pTargetSoldier->uiStatusFlags |= SOLDIER_ENGAGEDINACTION;
  1236. return;
  1237. }
  1238. else
  1239. {
  1240. return;
  1241. }
  1242. }
  1243. BOOLEAN SoldierDropItem( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObj )
  1244. {
  1245. pSoldier->pTempObject = MemAlloc( sizeof( OBJECTTYPE ) );
  1246. if (pSoldier->pTempObject == NULL)
  1247. {
  1248. // OUT OF MEMORY! YIKES!
  1249. return( FALSE );
  1250. }
  1251. memcpy( pSoldier->pTempObject, pObj, sizeof( OBJECTTYPE ) );
  1252. PickDropItemAnimation( pSoldier );
  1253. return( TRUE );
  1254. }
  1255. void SoldierPickupItem( SOLDIERTYPE *pSoldier, INT32 iItemIndex, INT16 sGridNo, INT8 bZLevel )
  1256. {
  1257. INT16 sActionGridNo;
  1258. // Remove any previous actions
  1259. pSoldier->ubPendingAction = NO_PENDING_ACTION;
  1260. sActionGridNo = AdjustGridNoForItemPlacement( pSoldier, sGridNo );
  1261. // SET PENDING ACTIONS!
  1262. pSoldier->ubPendingAction = MERC_PICKUPITEM;
  1263. pSoldier->uiPendingActionData1 = iItemIndex;
  1264. pSoldier->sPendingActionData2 = sActionGridNo;
  1265. pSoldier->uiPendingActionData4 = sGridNo;
  1266. pSoldier->bPendingActionData3 = bZLevel;
  1267. pSoldier->ubPendingActionAnimCount = 0;
  1268. // Deduct points!
  1269. //sAPCost = GetAPsToPickupItem( pSoldier, sGridNo );
  1270. //DeductPoints( pSoldier, sAPCost, 0 );
  1271. SetUIBusy( pSoldier->ubID );
  1272. // CHECK IF NOT AT SAME GRIDNO
  1273. if ( pSoldier->sGridNo != sActionGridNo )
  1274. {
  1275. if ( pSoldier->bTeam == gbPlayerNum )
  1276. {
  1277. EVENT_InternalGetNewSoldierPath( pSoldier, sActionGridNo, pSoldier->usUIMovementMode, TRUE, TRUE );
  1278. // Say it only if we don;t have to go too far!
  1279. if ( pSoldier->usPathDataSize > 5 )
  1280. {
  1281. DoMercBattleSound( pSoldier, BATTLE_SOUND_OK1 );
  1282. }
  1283. }
  1284. else
  1285. {
  1286. EVENT_InternalGetNewSoldierPath( pSoldier, sActionGridNo, pSoldier->usUIMovementMode, FALSE, TRUE );
  1287. }
  1288. }
  1289. else
  1290. {
  1291. // DO ANIMATION OF PICKUP NOW!
  1292. PickPickupAnimation( pSoldier, pSoldier->uiPendingActionData1, (INT16)( pSoldier->uiPendingActionData4 ), pSoldier->bPendingActionData3 );
  1293. }
  1294. }
  1295. void HandleAutoPlaceFail( SOLDIERTYPE *pSoldier, INT32 iItemIndex, INT16 sGridNo )
  1296. {
  1297. if (pSoldier->bTeam == gbPlayerNum)
  1298. {
  1299. // Place it in buddy's hand!
  1300. if ( gpItemPointer == NULL )
  1301. {
  1302. InternalBeginItemPointer( pSoldier, &(gWorldItems[ iItemIndex ].o ), NO_SLOT );
  1303. }
  1304. else
  1305. {
  1306. // Add back to world...
  1307. AddItemToPool( sGridNo, &(gWorldItems[ iItemIndex ].o ), 1 , pSoldier->bLevel, 0, -1 );
  1308. // If we are a merc, say DAMN quote....
  1309. if ( pSoldier->bTeam == gbPlayerNum )
  1310. {
  1311. DoMercBattleSound( pSoldier, BATTLE_SOUND_CURSE1 );
  1312. }
  1313. }
  1314. }
  1315. }
  1316. void SoldierGetItemFromWorld( SOLDIERTYPE *pSoldier, INT32 iItemIndex, INT16 sGridNo, INT8 bZLevel, BOOLEAN *pfSelectionList )
  1317. {
  1318. ITEM_POOL * pItemPool;
  1319. ITEM_POOL * pItemPoolToDelete = NULL;
  1320. OBJECTTYPE Object;
  1321. INT32 cnt = 0;
  1322. BOOLEAN fPickup;
  1323. BOOLEAN fFailedAutoPlace = FALSE;
  1324. INT32 iItemIndexToDelete;
  1325. BOOLEAN fShouldSayCoolQuote = FALSE;
  1326. BOOLEAN fDidSayCoolQuote = FALSE;
  1327. BOOLEAN fSaidBoobyTrapQuote = FALSE;
  1328. // OK. CHECK IF WE ARE DOING ALL IN THIS POOL....
  1329. if ( iItemIndex == ITEM_PICKUP_ACTION_ALL || iItemIndex == ITEM_PICKUP_SELECTION )
  1330. {
  1331. // DO all pickup!
  1332. // LOOP THROUGH LIST TO FIND NODE WE WANT
  1333. GetItemPool( sGridNo, &pItemPool, pSoldier->bLevel );
  1334. while( pItemPool )
  1335. {
  1336. if ( ItemPoolOKForPickup( pSoldier, pItemPool, bZLevel ) )
  1337. {
  1338. fPickup = TRUE;
  1339. if ( iItemIndex == ITEM_PICKUP_SELECTION )
  1340. {
  1341. if ( !pfSelectionList[ cnt ] )
  1342. {
  1343. fPickup = FALSE;
  1344. }
  1345. }
  1346. // Increment counter...
  1347. //:ATE: Only incremrnt counter for items we can see..
  1348. cnt++;
  1349. if ( fPickup )
  1350. {
  1351. if ( ContinuePastBoobyTrap( pSoldier, sGridNo, bZLevel, pItemPool->iItemIndex, FALSE, &fSaidBoobyTrapQuote ) )
  1352. {
  1353. // Make copy of item
  1354. memcpy( &Object, &(gWorldItems[ pItemPool->iItemIndex ].o), sizeof( OBJECTTYPE ) );
  1355. if ( ItemIsCool( &Object ) )
  1356. {
  1357. fShouldSayCoolQuote = TRUE;
  1358. }
  1359. if (Object.usItem == SWITCH)
  1360. {
  1361. // ask about activating the switch!
  1362. bTempFrequency = Object.bFrequency;
  1363. gpTempSoldier = pSoldier;
  1364. DoMessageBox( MSG_BOX_BASIC_STYLE, TacticalStr[ ACTIVATE_SWITCH_PROMPT ] , GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_YESNO, SwitchMessageBoxCallBack, NULL );
  1365. pItemPool = pItemPool->pNext;
  1366. }
  1367. else
  1368. {
  1369. if ( !AutoPlaceObject( pSoldier, &Object, TRUE ) )
  1370. {
  1371. // check to see if the object has been swapped with one in inventory
  1372. if ( Object.usItem != gWorldItems[ pItemPool->iItemIndex ].o.usItem || Object.ubNumberOfObjects != gWorldItems[ pItemPool->iItemIndex ].o.ubNumberOfObjects )
  1373. {
  1374. // copy back because item changed, and we must make sure the item pool reflects this.
  1375. memcpy( &(gWorldItems[ pItemPool->iItemIndex ].o), &Object, sizeof( OBJECTTYPE ) );
  1376. }
  1377. pItemPoolToDelete = pItemPool;
  1378. pItemPool = pItemPool->pNext;
  1379. fFailedAutoPlace = TRUE;
  1380. // continue, to try and place ay others...
  1381. continue;
  1382. }
  1383. /*
  1384. // handle theft.. will return true if theft has failed ( if soldier was caught )
  1385. if( pSoldier->bTeam == OUR_TEAM )
  1386. {
  1387. // check to see if object was owned by another
  1388. if( Object.fFlags & OBJECT_OWNED_BY_CIVILIAN )
  1389. {
  1390. // owned by a civilian
  1391. if( HandleLoyaltyAdjustmentForRobbery( pSoldier ) == TRUE )
  1392. {
  1393. // implememnt actual tactical reaction for theft..shoot robber, yell out, etc
  1394. }
  1395. // reset who owns object
  1396. Object.fFlags &= ~( OBJECT_OWNED_BY_CIVILIAN );
  1397. }
  1398. }
  1399. */
  1400. //pItemPoolToDelete = pItemPool;
  1401. iItemIndexToDelete = pItemPool->iItemIndex;
  1402. pItemPool = pItemPool->pNext;
  1403. RemoveItemFromPool( sGridNo, iItemIndexToDelete, pSoldier->bLevel );
  1404. }
  1405. }
  1406. else
  1407. {
  1408. // boobytrap found... stop picking up things!
  1409. break;
  1410. }
  1411. }
  1412. else
  1413. {
  1414. pItemPool = pItemPool->pNext;
  1415. }
  1416. }
  1417. else
  1418. {
  1419. pItemPool = pItemPool->pNext;
  1420. }
  1421. }
  1422. // ATE; If here, and we failed to add any more stuff, put failed one in our cursor...
  1423. if ( pItemPoolToDelete != NULL && fFailedAutoPlace )
  1424. {
  1425. gfDontChargeAPsToPickup = TRUE;
  1426. HandleAutoPlaceFail( pSoldier, pItemPoolToDelete->iItemIndex, sGridNo );
  1427. RemoveItemFromPool( sGridNo, pItemPoolToDelete->iItemIndex, pSoldier->bLevel );
  1428. pItemPoolToDelete = NULL;
  1429. }
  1430. }
  1431. else
  1432. {
  1433. // REMOVE ITEM FROM POOL
  1434. if ( ItemExistsAtLocation( sGridNo, iItemIndex, pSoldier->bLevel ) )
  1435. {
  1436. if ( ContinuePastBoobyTrap( pSoldier, sGridNo, bZLevel, iItemIndex, FALSE, &fSaidBoobyTrapQuote ) )
  1437. {
  1438. // Make copy of item
  1439. memcpy( &Object, &(gWorldItems[ iItemIndex ].o), sizeof( OBJECTTYPE ) );
  1440. if ( ItemIsCool( &Object ) )
  1441. {
  1442. fShouldSayCoolQuote = TRUE;
  1443. }
  1444. if (Object.usItem == SWITCH)
  1445. {
  1446. // handle switch
  1447. bTempFrequency = Object.bFrequency;
  1448. gpTempSoldier = pSoldier;
  1449. DoMessageBox( MSG_BOX_BASIC_STYLE, TacticalStr[ ACTIVATE_SWITCH_PROMPT ], GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_YESNO, SwitchMessageBoxCallBack, NULL );
  1450. }
  1451. else
  1452. {
  1453. /*
  1454. // handle theft.. will return true if theft has failed ( if soldier was caught )
  1455. if( pSoldier->bTeam == OUR_TEAM )
  1456. {
  1457. // check to see if object was owned by another
  1458. if( Object.fFlags & OBJECT_OWNED_BY_CIVILIAN )
  1459. {
  1460. // owned by a civilian
  1461. if( HandleLoyaltyAdjustmentForRobbery( pSoldier ) == TRUE )
  1462. {
  1463. // implememnt actual tactical reaction for theft..shoot robber, yell out, etc
  1464. }
  1465. // reset who owns object
  1466. Object.fFlags &= ~( OBJECT_OWNED_BY_CIVILIAN );
  1467. }
  1468. }
  1469. */
  1470. RemoveItemFromPool( sGridNo, iItemIndex, pSoldier->bLevel );
  1471. if ( !AutoPlaceObject( pSoldier, &(gWorldItems[ iItemIndex ].o ), TRUE ) )
  1472. {
  1473. gfDontChargeAPsToPickup = TRUE;
  1474. HandleAutoPlaceFail( pSoldier, iItemIndex, sGridNo );
  1475. }
  1476. }
  1477. }
  1478. }
  1479. }
  1480. // OK, check if potentially a good candidate for cool quote
  1481. if ( fShouldSayCoolQuote && pSoldier->bTeam == gbPlayerNum )
  1482. {
  1483. // Do we have this quote..?
  1484. if ( QuoteExp_GotGunOrUsedGun[ pSoldier->ubProfile ] == QUOTE_FOUND_SOMETHING_SPECIAL )
  1485. {
  1486. // Have we not said it today?
  1487. if ( !( pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_FOUND_SOMETHING_NICE ) )
  1488. {
  1489. // set flag
  1490. pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_FOUND_SOMETHING_NICE;
  1491. // Say it....
  1492. // We've found something!
  1493. TacticalCharacterDialogue( pSoldier, QUOTE_FOUND_SOMETHING_SPECIAL );
  1494. fDidSayCoolQuote = TRUE;
  1495. }
  1496. }
  1497. }
  1498. // Aknowledge....
  1499. if( pSoldier->bTeam == OUR_TEAM && !fDidSayCoolQuote && !fSaidBoobyTrapQuote )
  1500. {
  1501. DoMercBattleSound( pSoldier, BATTLE_SOUND_GOTIT );
  1502. }
  1503. // OK partner......look for any hidden items!
  1504. if ( pSoldier->bTeam == gbPlayerNum && LookForHiddenItems( sGridNo, pSoldier->bLevel, TRUE, 0 ) )
  1505. {
  1506. // WISDOM GAIN (5): Found a hidden object
  1507. StatChange( pSoldier, WISDOMAMT, 5, FALSE );
  1508. // We've found something!
  1509. TacticalCharacterDialogue( pSoldier, (UINT16)( QUOTE_SPOTTED_SOMETHING_ONE + Random( 2 ) ) );
  1510. }
  1511. gpTempSoldier = pSoldier;
  1512. gsTempGridno = sGridNo;
  1513. SetCustomizableTimerCallbackAndDelay( 1000, CheckForPickedOwnership, TRUE );
  1514. }
  1515. void HandleSoldierPickupItem( SOLDIERTYPE *pSoldier, INT32 iItemIndex, INT16 sGridNo, INT8 bZLevel )
  1516. {
  1517. ITEM_POOL *pItemPool;
  1518. UINT16 usNum;
  1519. // Draw menu if more than one item!
  1520. if ( GetItemPool( sGridNo, &pItemPool, pSoldier->bLevel ) )
  1521. {
  1522. // OK, if an enemy, go directly ( skip menu )
  1523. if ( pSoldier->bTeam != gbPlayerNum )
  1524. {
  1525. SoldierGetItemFromWorld( pSoldier, iItemIndex, sGridNo, bZLevel, NULL );
  1526. }
  1527. else
  1528. {
  1529. if (gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_PLAYER_MINE_PRESENT)
  1530. {
  1531. // have the computer ask us if we want to proceed
  1532. // override the item index passed in with the one for the bomb in this
  1533. // tile
  1534. iItemIndex = FindWorldItemForBombInGridNo( sGridNo, pSoldier->bLevel );
  1535. #ifdef JA2TESTVERSION
  1536. if (iItemIndex == -1)
  1537. {
  1538. // WTF????
  1539. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_ERROR, L"Cannot find bomb item in gridno %d", sGridNo );
  1540. return;
  1541. }
  1542. #endif
  1543. gpBoobyTrapItemPool = GetItemPoolForIndex( sGridNo, iItemIndex, pSoldier->bLevel );
  1544. gpBoobyTrapSoldier = pSoldier;
  1545. gsBoobyTrapGridNo = sGridNo;
  1546. gbBoobyTrapLevel = pSoldier->bLevel;
  1547. gfDisarmingBuriedBomb = TRUE;
  1548. gbTrapDifficulty = gWorldItems[ iItemIndex ].o.bTrap;
  1549. DoMessageBox( MSG_BOX_BASIC_STYLE, TacticalStr[ DISARM_TRAP_PROMPT ], GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_YESNO, BoobyTrapMessageBoxCallBack, NULL );
  1550. }
  1551. else
  1552. {
  1553. // OK, only hidden items exist...
  1554. if ( pSoldier->bTeam == gbPlayerNum && DoesItemPoolContainAllHiddenItems( pItemPool ) )
  1555. {
  1556. // He's touched them....
  1557. if ( LookForHiddenItems( sGridNo, pSoldier->bLevel, TRUE, 0 ) )
  1558. {
  1559. // WISDOM GAIN (5): Found a hidden object
  1560. StatChange( pSoldier, WISDOMAMT, 5, FALSE );
  1561. // We've found something!
  1562. TacticalCharacterDialogue( pSoldier, (UINT16)( QUOTE_SPOTTED_SOMETHING_ONE + Random( 2 ) ) );
  1563. }
  1564. else
  1565. {
  1566. // Say NOTHING quote...
  1567. DoMercBattleSound( pSoldier, BATTLE_SOUND_NOTHING );
  1568. }
  1569. }
  1570. else
  1571. {
  1572. // If only one good item exists....
  1573. if ( ( usNum = GetNumOkForDisplayItemsInPool( pItemPool, bZLevel ) ) == 1 )
  1574. {
  1575. // Find first OK item....
  1576. while( !ItemPoolOKForDisplay( pItemPool, bZLevel ) )
  1577. {
  1578. pItemPool = pItemPool->pNext;
  1579. }
  1580. SoldierGetItemFromWorld( pSoldier, pItemPool->iItemIndex, sGridNo, bZLevel, NULL );
  1581. }
  1582. else
  1583. {
  1584. if ( usNum != 0 )
  1585. {
  1586. // Freeze guy!
  1587. pSoldier->fPauseAllAnimation = TRUE;
  1588. InitializeItemPickupMenu( pSoldier, sGridNo, pItemPool, 0, 0, bZLevel );
  1589. guiPendingOverrideEvent = G_GETTINGITEM;
  1590. }
  1591. else
  1592. {
  1593. DoMercBattleSound( pSoldier, BATTLE_SOUND_NOTHING );
  1594. }
  1595. }
  1596. }
  1597. }
  1598. }
  1599. }
  1600. else
  1601. {
  1602. // Say NOTHING quote...
  1603. DoMercBattleSound( pSoldier, BATTLE_SOUND_NOTHING );
  1604. }
  1605. }
  1606. LEVELNODE *AddItemGraphicToWorld( INVTYPE *pItem, INT16 sGridNo, UINT8 ubLevel )
  1607. {
  1608. UINT16 usTileIndex;
  1609. LEVELNODE *pNode;
  1610. usTileIndex = GetTileGraphicForItem( pItem );
  1611. // OK, Do stuff differently base on level!
  1612. if ( ubLevel == 0 )
  1613. {
  1614. pNode = AddStructToTail( sGridNo, usTileIndex );
  1615. //SET FLAG FOR AN ITEM
  1616. pNode->uiFlags |= LEVELNODE_ITEM;
  1617. }
  1618. else
  1619. {
  1620. AddOnRoofToHead( sGridNo, usTileIndex );
  1621. //SET FLAG FOR AN ITEM
  1622. pNode = gpWorldLevelData[ sGridNo ].pOnRoofHead;
  1623. pNode->uiFlags |= LEVELNODE_ITEM;
  1624. }
  1625. // DIRTY INTERFACE
  1626. fInterfacePanelDirty = DIRTYLEVEL2;
  1627. // DIRTY TILE
  1628. gpWorldLevelData[ sGridNo ].uiFlags |= MAPELEMENT_REDRAW;
  1629. SetRenderFlags(RENDER_FLAG_MARKED);
  1630. return( pNode );
  1631. }
  1632. void RemoveItemGraphicFromWorld( INVTYPE *pItem, INT16 sGridNo, UINT8 ubLevel, LEVELNODE *pLevelNode )
  1633. {
  1634. LEVELNODE *pNode;
  1635. // OK, Do stuff differently base on level!
  1636. // Loop through and find pointer....
  1637. if ( ubLevel == 0 )
  1638. {
  1639. pNode = gpWorldLevelData[ sGridNo ].pStructHead;
  1640. }
  1641. else
  1642. {
  1643. pNode = gpWorldLevelData[ sGridNo ].pOnRoofHead;
  1644. }
  1645. while( pNode != NULL )
  1646. {
  1647. if ( pNode == pLevelNode )
  1648. {
  1649. // Found one!
  1650. if ( ubLevel == 0 )
  1651. {
  1652. RemoveStructFromLevelNode( sGridNo, pNode );
  1653. }
  1654. else
  1655. {
  1656. RemoveOnRoofFromLevelNode( sGridNo, pNode );
  1657. }
  1658. break;
  1659. }
  1660. pNode = pNode->pNext;
  1661. }
  1662. // DIRTY INTERFACE
  1663. fInterfacePanelDirty = DIRTYLEVEL2;
  1664. // DIRTY TILE
  1665. gpWorldLevelData[ sGridNo ].uiFlags |= MAPELEMENT_REDRAW;
  1666. SetRenderFlags(RENDER_FLAG_MARKED);
  1667. //TEMP RENDER FULL!!!
  1668. SetRenderFlags(RENDER_FLAG_FULL);
  1669. }
  1670. // INVENTORY POOL STUFF
  1671. OBJECTTYPE* AddItemToPool( INT16 sGridNo, OBJECTTYPE *pObject, INT8 bVisible, UINT8 ubLevel, UINT16 usFlags, INT8 bRenderZHeightAboveLevel )
  1672. {
  1673. return InternalAddItemToPool( &sGridNo, pObject, bVisible, ubLevel, usFlags, bRenderZHeightAboveLevel, NULL );
  1674. }
  1675. OBJECTTYPE * AddItemToPoolAndGetIndex( INT16 sGridNo, OBJECTTYPE *pObject, INT8 bVisible, UINT8 ubLevel, UINT16 usFlags, INT8 bRenderZHeightAboveLevel, INT32 * piItemIndex )
  1676. {
  1677. return( InternalAddItemToPool( &sGridNo, pObject, bVisible, ubLevel, usFlags, bRenderZHeightAboveLevel, piItemIndex ) );
  1678. }
  1679. OBJECTTYPE* InternalAddItemToPool( INT16 *psGridNo, OBJECTTYPE *pObject, INT8 bVisible, UINT8 ubLevel, UINT16 usFlags, INT8 bRenderZHeightAboveLevel, INT32 * piItemIndex )
  1680. {
  1681. ITEM_POOL *pItemPool;
  1682. ITEM_POOL *pItemPoolTemp;
  1683. INT32 iWorldItem;
  1684. STRUCTURE *pStructure, *pBase;
  1685. INT16 sDesiredLevel;
  1686. INT16 sNewGridNo = *psGridNo;
  1687. LEVELNODE *pNode;
  1688. BOOLEAN fForceOnGround = FALSE;
  1689. BOOLEAN fObjectInOpenable = FALSE;
  1690. INT8 bTerrainID;
  1691. Assert( pObject->ubNumberOfObjects <= MAX_OBJECTS_PER_SLOT);
  1692. // ATE: Check if the gridno is OK
  1693. if ( (*psGridNo) == NOWHERE )
  1694. {
  1695. // Display warning.....
  1696. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"Error: Item %d was given invalid grid location %d for item pool. Please Report.", pObject->usItem, (*psGridNo) );
  1697. (*psGridNo) = sNewGridNo = gMapInformation.sCenterGridNo;
  1698. //return( NULL );
  1699. }
  1700. // CHECK IF THIS ITEM IS IN DEEP WATER....
  1701. // IF SO, CHECK IF IT SINKS...
  1702. // IF SO, DONT'T ADD!
  1703. bTerrainID = GetTerrainType( *psGridNo );
  1704. if ( bTerrainID == DEEP_WATER || bTerrainID == LOW_WATER || bTerrainID == MED_WATER )
  1705. {
  1706. if ( Item[ pObject->usItem ].fFlags & ITEM_SINKS )
  1707. {
  1708. return( NULL );
  1709. }
  1710. }
  1711. // First things first - look at where we are to place the items, and
  1712. // set some flags appropriately
  1713. // On a structure?
  1714. //Locations on roofs without a roof is not possible, so
  1715. //we convert the onroof intention to ground.
  1716. if( ubLevel && !FlatRoofAboveGridNo( *psGridNo ) )
  1717. {
  1718. ubLevel = 0;
  1719. }
  1720. if ( bRenderZHeightAboveLevel == -1 )
  1721. {
  1722. fForceOnGround = TRUE;
  1723. bRenderZHeightAboveLevel = 0;
  1724. }
  1725. // Check structure database
  1726. if ( gpWorldLevelData[ *psGridNo ].pStructureHead && (pObject->usItem != OWNERSHIP) && (pObject->usItem != ACTION_ITEM) )
  1727. {
  1728. // Something is here, check obstruction in future
  1729. sDesiredLevel = ubLevel ? STRUCTURE_ON_ROOF : STRUCTURE_ON_GROUND;
  1730. pStructure = FindStructure( *psGridNo , STRUCTURE_BLOCKSMOVES );
  1731. while( pStructure )
  1732. {
  1733. if( !(pStructure->fFlags &( STRUCTURE_PERSON | STRUCTURE_CORPSE ) ) && pStructure->sCubeOffset == sDesiredLevel )
  1734. {
  1735. // If we are going into a raised struct AND we have above level set to -1
  1736. if ( StructureBottomLevel( pStructure ) != 1 && fForceOnGround )
  1737. {
  1738. break;
  1739. }
  1740. // Adjust the item's gridno to the base of struct.....
  1741. pBase = FindBaseStructure( pStructure );
  1742. // Get LEVELNODE for struct and remove!
  1743. sNewGridNo = pBase->sGridNo;
  1744. // Check for openable flag....
  1745. if ( pStructure->fFlags & STRUCTURE_OPENABLE )
  1746. {
  1747. // ATE: Set a flag here - we need to know later that we're in an openable...
  1748. fObjectInOpenable = TRUE;
  1749. // Something of note is here....
  1750. // SOME sort of structure is here.... set render flag to off
  1751. usFlags |= WORLD_ITEM_DONTRENDER;
  1752. // Openable.. check if it's closed, if so, set visiblity...
  1753. if ( !( pStructure->fFlags & STRUCTURE_OPEN ) )
  1754. {
  1755. // -2 means - don't reveal!
  1756. bVisible = -2;
  1757. }
  1758. bRenderZHeightAboveLevel = CONVERT_INDEX_TO_PIXELS( StructureHeight( pStructure ) );
  1759. break;
  1760. }
  1761. // Else can we place an item on top?
  1762. else if ( pStructure->fFlags & ( STRUCTURE_GENERIC ) )
  1763. {
  1764. UINT8 ubLevel0, ubLevel1, ubLevel2, ubLevel3;
  1765. // If we are going into a raised struct AND we have above level set to -1
  1766. if ( StructureBottomLevel( pStructure ) != 1 && fForceOnGround )
  1767. {
  1768. break;
  1769. }
  1770. // Find most dence area...
  1771. if ( StructureDensity( pStructure, &ubLevel0, &ubLevel1, &ubLevel2, &ubLevel3 ) )
  1772. {
  1773. if ( ubLevel3 == 0 && ubLevel2 == 0 && ubLevel1 == 0 && ubLevel0 == 0 )
  1774. {
  1775. bRenderZHeightAboveLevel = 0;
  1776. }
  1777. else if ( ubLevel3 >= ubLevel0 && ubLevel3 >= ubLevel2 && ubLevel3 >= ubLevel1 )
  1778. {
  1779. bRenderZHeightAboveLevel = CONVERT_INDEX_TO_PIXELS( 4 );
  1780. }
  1781. else if ( ubLevel2 >= ubLevel0 && ubLevel2 >= ubLevel1 && ubLevel2 >= ubLevel3 )
  1782. {
  1783. bRenderZHeightAboveLevel = CONVERT_INDEX_TO_PIXELS( 3 );
  1784. }
  1785. else if ( ubLevel1 >= ubLevel0 && ubLevel1 >= ubLevel2 && ubLevel1 >= ubLevel3 )
  1786. {
  1787. bRenderZHeightAboveLevel = CONVERT_INDEX_TO_PIXELS( 2 );
  1788. }
  1789. else if ( ubLevel0 >= ubLevel1 && ubLevel0 >= ubLevel2 && ubLevel0 >= ubLevel3 )
  1790. {
  1791. bRenderZHeightAboveLevel = CONVERT_INDEX_TO_PIXELS( 1 );
  1792. }
  1793. }
  1794. // Set flag indicating it has an item on top!
  1795. pStructure->fFlags |= STRUCTURE_HASITEMONTOP;
  1796. break;
  1797. }
  1798. }
  1799. pStructure = FindNextStructure( pStructure, STRUCTURE_BLOCKSMOVES );
  1800. }
  1801. }
  1802. if (pObject->usItem == SWITCH && !fObjectInOpenable )
  1803. {
  1804. if (bVisible != -2)
  1805. {
  1806. // switch items which are not hidden inside objects should be considered buried
  1807. bVisible = BURIED;
  1808. // and they are pressure-triggered unless there is a switch structure there
  1809. if (FindStructure( *psGridNo, STRUCTURE_SWITCH ) != NULL)
  1810. {
  1811. pObject->bDetonatorType = BOMB_SWITCH;
  1812. }
  1813. else
  1814. {
  1815. pObject->bDetonatorType = BOMB_PRESSURE;
  1816. }
  1817. }
  1818. else
  1819. {
  1820. // else they are manually controlled
  1821. pObject->bDetonatorType = BOMB_SWITCH;
  1822. }
  1823. }
  1824. else if ( pObject->usItem == ACTION_ITEM )
  1825. {
  1826. switch( pObject->bActionValue )
  1827. {
  1828. case ACTION_ITEM_SMALL_PIT:
  1829. case ACTION_ITEM_LARGE_PIT:
  1830. // mark as known about by civs and creatures
  1831. gpWorldLevelData[ sNewGridNo ].uiFlags |= MAPELEMENT_ENEMY_MINE_PRESENT;
  1832. break;
  1833. default:
  1834. break;
  1835. }
  1836. }
  1837. if ( *psGridNo != sNewGridNo )
  1838. {
  1839. *psGridNo = sNewGridNo;
  1840. }
  1841. //First add the item to the global list. This is so the game can keep track
  1842. //of where the items are, for file i/o, etc.
  1843. iWorldItem = AddItemToWorld( *psGridNo, pObject, ubLevel, usFlags, bRenderZHeightAboveLevel, bVisible );
  1844. // Check for and existing pool on the object layer
  1845. if ( GetItemPool( *psGridNo, &pItemPool, ubLevel ) )
  1846. {
  1847. // Add to exitsing pool
  1848. // Add graphic
  1849. pNode = AddItemGraphicToWorld( &(Item[ pObject->usItem ] ), *psGridNo, ubLevel );
  1850. // Set pool head value in levelnode
  1851. pNode->pItemPool = pItemPool;
  1852. // Add New Node
  1853. pItemPoolTemp = pItemPool;
  1854. // Create new pool
  1855. pItemPool = MemAlloc( sizeof( ITEM_POOL ) );
  1856. // Set Next to NULL
  1857. pItemPool->pNext = NULL;
  1858. // Set Item index
  1859. pItemPool->iItemIndex = iWorldItem;
  1860. // Get a link back!
  1861. pItemPool->pLevelNode = pNode;
  1862. if( pItemPoolTemp )
  1863. {
  1864. // Get last item in list
  1865. while( pItemPoolTemp->pNext != NULL )
  1866. pItemPoolTemp = pItemPoolTemp->pNext;
  1867. // Set Next of previous
  1868. pItemPoolTemp->pNext = pItemPool;
  1869. }
  1870. // Set Previous of new one
  1871. pItemPool->pPrev = pItemPoolTemp;
  1872. }
  1873. else
  1874. {
  1875. pNode = AddItemGraphicToWorld( &(Item[ pObject->usItem ] ), *psGridNo, ubLevel );
  1876. // Create new pool
  1877. pItemPool = MemAlloc( sizeof( ITEM_POOL ) );
  1878. pNode->pItemPool = pItemPool;
  1879. // Set prev to NULL
  1880. pItemPool->pPrev = NULL;
  1881. // Set next to NULL
  1882. pItemPool->pNext = NULL;
  1883. // Set Item index
  1884. pItemPool->iItemIndex = iWorldItem;
  1885. // Get a link back!
  1886. pItemPool->pLevelNode = pNode;
  1887. // Set flag to indicate item pool presence
  1888. gpWorldLevelData[*psGridNo].uiFlags |= MAPELEMENT_ITEMPOOL_PRESENT;
  1889. }
  1890. // Set visible!
  1891. pItemPool->bVisible = bVisible;
  1892. // If bbisible is true, render makered world
  1893. if ( bVisible == 1 && GridNoOnScreen( (*psGridNo ) ) )
  1894. {
  1895. //gpWorldLevelData[*psGridNo].uiFlags|=MAPELEMENT_REDRAW;
  1896. //SetRenderFlags(RENDER_FLAG_MARKED);
  1897. SetRenderFlags(RENDER_FLAG_FULL);
  1898. }
  1899. // Set flahs timer
  1900. pItemPool->bFlashColor = FALSE;
  1901. pItemPool->sGridNo = *psGridNo;
  1902. pItemPool->ubLevel = ubLevel;
  1903. pItemPool->usFlags = usFlags;
  1904. pItemPool->bVisible = bVisible;
  1905. pItemPool->bRenderZHeightAboveLevel = bRenderZHeightAboveLevel;
  1906. // ATE: Get head of pool again....
  1907. if ( GetItemPool( *psGridNo, &pItemPool, ubLevel ) )
  1908. {
  1909. AdjustItemPoolVisibility( pItemPool );
  1910. }
  1911. if (piItemIndex)
  1912. {
  1913. *piItemIndex = iWorldItem;
  1914. }
  1915. return( &(gWorldItems[ iWorldItem ].o ) );
  1916. }
  1917. BOOLEAN ItemExistsAtLocation( INT16 sGridNo, INT32 iItemIndex, UINT8 ubLevel )
  1918. {
  1919. ITEM_POOL *pItemPool;
  1920. ITEM_POOL *pItemPoolTemp;
  1921. BOOLEAN fItemFound = FALSE;
  1922. // Check for an existing pool on the object layer
  1923. if ( GetItemPool( sGridNo, &pItemPool, ubLevel ) )
  1924. {
  1925. // LOOP THROUGH LIST TO FIND NODE WE WANT
  1926. pItemPoolTemp = pItemPool;
  1927. while( pItemPoolTemp != NULL )
  1928. {
  1929. if ( pItemPoolTemp->iItemIndex == iItemIndex )
  1930. {
  1931. return( TRUE );
  1932. }
  1933. pItemPoolTemp = pItemPoolTemp->pNext;
  1934. }
  1935. }
  1936. return( FALSE );
  1937. }
  1938. BOOLEAN ItemTypeExistsAtLocation( INT16 sGridNo, UINT16 usItem, UINT8 ubLevel, INT32 * piItemIndex )
  1939. {
  1940. ITEM_POOL *pItemPool;
  1941. ITEM_POOL *pItemPoolTemp;
  1942. BOOLEAN fItemFound = FALSE;
  1943. // Check for an existing pool on the object layer
  1944. if ( GetItemPool( sGridNo, &pItemPool, ubLevel ) )
  1945. {
  1946. // LOOP THROUGH LIST TO FIND ITEM WE WANT
  1947. pItemPoolTemp = pItemPool;
  1948. while( pItemPoolTemp != NULL )
  1949. {
  1950. if ( gWorldItems[ pItemPoolTemp->iItemIndex ].o.usItem == usItem )
  1951. {
  1952. if ( piItemIndex )
  1953. {
  1954. *piItemIndex = pItemPoolTemp->iItemIndex;
  1955. }
  1956. return( TRUE );
  1957. }
  1958. pItemPoolTemp = pItemPoolTemp->pNext;
  1959. }
  1960. }
  1961. return( FALSE );
  1962. }
  1963. INT32 GetItemOfClassTypeInPool( INT16 sGridNo, UINT32 uiItemClass, UINT8 ubLevel )
  1964. {
  1965. ITEM_POOL *pItemPool;
  1966. ITEM_POOL *pItemPoolTemp;
  1967. BOOLEAN fItemFound = FALSE;
  1968. // Check for an existing pool on the object layer
  1969. if ( GetItemPool( sGridNo, &pItemPool, ubLevel ) )
  1970. {
  1971. // LOOP THROUGH LIST TO FIND NODE WE WANT
  1972. pItemPoolTemp = pItemPool;
  1973. while( pItemPoolTemp != NULL )
  1974. {
  1975. if ( Item[ gWorldItems[ pItemPoolTemp->iItemIndex ].o.usItem ].usItemClass & uiItemClass )
  1976. {
  1977. return( pItemPoolTemp->iItemIndex );
  1978. }
  1979. pItemPoolTemp = pItemPoolTemp->pNext;
  1980. }
  1981. }
  1982. return( -1 );
  1983. }
  1984. ITEM_POOL * GetItemPoolForIndex( INT16 sGridNo, INT32 iItemIndex, UINT8 ubLevel )
  1985. {
  1986. ITEM_POOL *pItemPool;
  1987. ITEM_POOL *pItemPoolTemp;
  1988. BOOLEAN fItemFound = FALSE;
  1989. // Check for an existing pool on the object layer
  1990. if ( GetItemPool( sGridNo, &pItemPool, ubLevel ) )
  1991. {
  1992. // LOOP THROUGH LIST TO FIND NODE WE WANT
  1993. pItemPoolTemp = pItemPool;
  1994. while( pItemPoolTemp != NULL )
  1995. {
  1996. if (pItemPoolTemp->iItemIndex == iItemIndex )
  1997. {
  1998. return( pItemPoolTemp );
  1999. }
  2000. pItemPoolTemp = pItemPoolTemp->pNext;
  2001. }
  2002. }
  2003. return( NULL );
  2004. }
  2005. BOOLEAN DoesItemPoolContainAnyHiddenItems( ITEM_POOL *pItemPool )
  2006. {
  2007. // LOOP THROUGH LIST TO FIND NODE WE WANT
  2008. while( pItemPool != NULL )
  2009. {
  2010. if ( gWorldItems[ pItemPool->iItemIndex ].bVisible == HIDDEN_ITEM )
  2011. {
  2012. return( TRUE );
  2013. }
  2014. pItemPool = pItemPool->pNext;
  2015. }
  2016. return( FALSE );
  2017. }
  2018. BOOLEAN DoesItemPoolContainAllHiddenItems( ITEM_POOL *pItemPool )
  2019. {
  2020. // LOOP THROUGH LIST TO FIND NODE WE WANT
  2021. while( pItemPool != NULL )
  2022. {
  2023. if ( gWorldItems[ pItemPool->iItemIndex ].bVisible != HIDDEN_ITEM )
  2024. {
  2025. return( FALSE );
  2026. }
  2027. pItemPool = pItemPool->pNext;
  2028. }
  2029. return( TRUE );
  2030. }
  2031. BOOLEAN LookForHiddenItems( INT16 sGridNo, INT8 ubLevel, BOOLEAN fSetLocator, INT8 bZLevel )
  2032. {
  2033. ITEM_POOL *pItemPool = NULL;
  2034. ITEM_POOL *pHeadItemPool = NULL;
  2035. BOOLEAN fFound = FALSE;
  2036. if ( GetItemPool( sGridNo, &pItemPool, ubLevel ) )
  2037. {
  2038. pHeadItemPool = pItemPool;
  2039. // LOOP THROUGH LIST TO FIND NODE WE WANT
  2040. while( pItemPool != NULL )
  2041. {
  2042. if ( gWorldItems[ pItemPool->iItemIndex ].bVisible == HIDDEN_ITEM && gWorldItems[ pItemPool->iItemIndex ].o.usItem != OWNERSHIP )
  2043. {
  2044. fFound = TRUE;
  2045. gWorldItems[ pItemPool->iItemIndex ].bVisible = INVISIBLE;
  2046. }
  2047. pItemPool = pItemPool->pNext;
  2048. }
  2049. }
  2050. // If found, set item pool visibility...
  2051. if ( fFound )
  2052. {
  2053. SetItemPoolVisibilityOn( pHeadItemPool, INVISIBLE, fSetLocator );
  2054. }
  2055. return( fFound );
  2056. }
  2057. INT8 GetZLevelOfItemPoolGivenStructure( INT16 sGridNo, UINT8 ubLevel, STRUCTURE *pStructure )
  2058. {
  2059. ITEM_POOL *pItemPool;
  2060. if ( pStructure == NULL )
  2061. {
  2062. return( 0 );
  2063. }
  2064. // OK, check if this struct contains items....
  2065. if ( GetItemPool( sGridNo, &pItemPool, ubLevel ) == TRUE )
  2066. {
  2067. return( GetLargestZLevelOfItemPool( pItemPool ) );
  2068. }
  2069. return( 0 );
  2070. }
  2071. INT8 GetLargestZLevelOfItemPool( ITEM_POOL *pItemPool )
  2072. {
  2073. // OK, loop through pools and get any height != 0........
  2074. while( pItemPool != NULL )
  2075. {
  2076. if ( pItemPool->bRenderZHeightAboveLevel > 0 )
  2077. {
  2078. return( pItemPool->bRenderZHeightAboveLevel );
  2079. }
  2080. pItemPool = pItemPool->pNext;
  2081. }
  2082. return( 0 );
  2083. }
  2084. BOOLEAN DoesItemPoolContainAllItemsOfHigherZLevel( ITEM_POOL *pItemPool )
  2085. {
  2086. // LOOP THROUGH LIST TO FIND NODE WE WANT
  2087. while( pItemPool != NULL )
  2088. {
  2089. if ( pItemPool->bRenderZHeightAboveLevel == 0 )
  2090. {
  2091. return( FALSE );
  2092. }
  2093. pItemPool = pItemPool->pNext;
  2094. }
  2095. return( TRUE );
  2096. }
  2097. BOOLEAN DoesItemPoolContainAllItemsOfZeroZLevel( ITEM_POOL *pItemPool )
  2098. {
  2099. // LOOP THROUGH LIST TO FIND NODE WE WANT
  2100. while( pItemPool != NULL )
  2101. {
  2102. if ( pItemPool->bRenderZHeightAboveLevel != 0 )
  2103. {
  2104. return( FALSE );
  2105. }
  2106. pItemPool = pItemPool->pNext;
  2107. }
  2108. return( TRUE );
  2109. }
  2110. void RemoveItemPool( INT16 sGridNo, UINT8 ubLevel )
  2111. {
  2112. ITEM_POOL *pItemPool;
  2113. // Check for and existing pool on the object layer
  2114. while ( GetItemPool( sGridNo, &pItemPool, ubLevel ) == TRUE )
  2115. {
  2116. RemoveItemFromPool( sGridNo, pItemPool->iItemIndex, ubLevel );
  2117. }
  2118. }
  2119. void RemoveAllUnburiedItems( INT16 sGridNo, UINT8 ubLevel )
  2120. {
  2121. ITEM_POOL *pItemPool;
  2122. // Check for and existing pool on the object layer
  2123. GetItemPool( sGridNo, &pItemPool, ubLevel );
  2124. while( pItemPool )
  2125. {
  2126. if ( gWorldItems[ pItemPool->iItemIndex ].bVisible == BURIED)
  2127. {
  2128. pItemPool = pItemPool->pNext;
  2129. }
  2130. else
  2131. {
  2132. RemoveItemFromPool( sGridNo, pItemPool->iItemIndex, ubLevel );
  2133. // get new start pointer
  2134. GetItemPool( sGridNo, &pItemPool, ubLevel );
  2135. }
  2136. }
  2137. }
  2138. void LoopLevelNodeForShowThroughFlag( LEVELNODE *pNode, INT16 sGridNo, UINT8 ubLevel )
  2139. {
  2140. while ( pNode != NULL )
  2141. {
  2142. if ( pNode->uiFlags & LEVELNODE_ITEM )
  2143. {
  2144. if ( ubLevel == 0 )
  2145. {
  2146. // If we are in a room....
  2147. // if ( IsRoofPresentAtGridno( sGridNo ) || gfCaves || gfBasement )
  2148. {
  2149. pNode->uiFlags |= LEVELNODE_SHOW_THROUGH;
  2150. }
  2151. }
  2152. else
  2153. {
  2154. pNode->uiFlags |= LEVELNODE_SHOW_THROUGH;
  2155. }
  2156. if( gGameSettings.fOptions[ TOPTION_GLOW_ITEMS ] )
  2157. {
  2158. pNode->uiFlags |= LEVELNODE_DYNAMIC;
  2159. }
  2160. }
  2161. pNode = pNode->pNext;
  2162. }
  2163. }
  2164. void HandleItemObscuredFlag( INT16 sGridNo, UINT8 ubLevel )
  2165. {
  2166. LEVELNODE *pNode;
  2167. if ( ubLevel == 0 )
  2168. {
  2169. pNode = gpWorldLevelData[ sGridNo ].pStructHead;
  2170. LoopLevelNodeForShowThroughFlag( pNode, sGridNo, ubLevel );
  2171. }
  2172. else
  2173. {
  2174. pNode = gpWorldLevelData[ sGridNo ].pOnRoofHead;
  2175. LoopLevelNodeForShowThroughFlag( pNode, sGridNo, ubLevel );
  2176. }
  2177. }
  2178. BOOLEAN SetItemPoolVisibilityOn( ITEM_POOL *pItemPool, INT8 bAllGreaterThan, BOOLEAN fSetLocator )
  2179. {
  2180. ITEM_POOL *pItemPoolTemp;
  2181. BOOLEAN fAtLeastModified = FALSE, fDeleted = FALSE;
  2182. INT8 bVisibleValue;
  2183. //OBJECTTYPE *pObj;
  2184. pItemPoolTemp = pItemPool;
  2185. while( pItemPoolTemp != NULL )
  2186. {
  2187. bVisibleValue = gWorldItems[ pItemPoolTemp->iItemIndex ].bVisible;
  2188. // Update each item...
  2189. if ( bVisibleValue != VISIBLE )
  2190. {
  2191. if ( gWorldItems[ pItemPoolTemp->iItemIndex ].o.usItem == ACTION_ITEM )
  2192. {
  2193. // NEVER MAKE VISIBLE!
  2194. pItemPoolTemp = pItemPoolTemp->pNext;
  2195. continue;
  2196. }
  2197. // If we have reached a visible value we should not modify, ignore...
  2198. if ( bVisibleValue >= bAllGreaterThan && gWorldItems[ pItemPoolTemp->iItemIndex ].o.usItem != OWNERSHIP )
  2199. {
  2200. // Update the world value
  2201. gWorldItems[ pItemPoolTemp->iItemIndex ].bVisible = VISIBLE;
  2202. fAtLeastModified = TRUE;
  2203. }
  2204. /*
  2205. if ( gWorldItems[ pItemPoolTemp->iItemIndex ].o.usItem == ACTION_ITEM )
  2206. {
  2207. pObj = &(gWorldItems[ pItemPoolTemp->iItemIndex ].o);
  2208. switch( pObj->bActionValue )
  2209. {
  2210. case ACTION_ITEM_SMALL_PIT:
  2211. case ACTION_ITEM_LARGE_PIT:
  2212. if (pObj->bDetonatorType == 0)
  2213. {
  2214. // randomly set to active or destroy the item!
  2215. if (Random( 100 ) < 65)
  2216. {
  2217. ArmBomb( pObj, 0 ); // will be set to pressure type so freq is irrelevant
  2218. gWorldItems[ pItemPoolTemp->iItemIndex ].usFlags |= WORLD_ITEM_ARMED_BOMB;
  2219. AddBombToWorld( pItemPoolTemp->iItemIndex );
  2220. }
  2221. else
  2222. {
  2223. // get pointer to the next element NOW
  2224. pItemPoolTemp = pItemPoolTemp->pNext;
  2225. // set flag so we don't traverse an additional time
  2226. fDeleted = TRUE;
  2227. // remove item from pool
  2228. RemoveItemFromPool( pItemPool->sGridNo, pItemPool->iItemIndex, pItemPool->ubLevel );
  2229. }
  2230. }
  2231. break;
  2232. default:
  2233. break;
  2234. }
  2235. }
  2236. */
  2237. if (fDeleted)
  2238. {
  2239. // don't get the 'next' pointer because we did so above
  2240. // reset fDeleted to false so we don't skip moving through the list more than once
  2241. fDeleted = FALSE;
  2242. }
  2243. else
  2244. {
  2245. pItemPoolTemp = pItemPoolTemp->pNext;
  2246. }
  2247. }
  2248. else
  2249. {
  2250. pItemPoolTemp = pItemPoolTemp->pNext;
  2251. }
  2252. }
  2253. // If we didn;t find any that should be modified..
  2254. if ( !fAtLeastModified )
  2255. {
  2256. return( FALSE );
  2257. }
  2258. // Update global pool bVisible to true ( if at least one is visible... )
  2259. pItemPoolTemp = pItemPool;
  2260. while( pItemPoolTemp != NULL )
  2261. {
  2262. pItemPoolTemp->bVisible = VISIBLE;
  2263. pItemPoolTemp = pItemPoolTemp->pNext;
  2264. }
  2265. // Handle obscured flag...
  2266. HandleItemObscuredFlag( pItemPool->sGridNo, pItemPool->ubLevel );
  2267. if ( fSetLocator )
  2268. {
  2269. SetItemPoolLocator( pItemPool );
  2270. }
  2271. return( TRUE );
  2272. }
  2273. void SetItemPoolVisibilityHidden( ITEM_POOL *pItemPool )
  2274. {
  2275. ITEM_POOL *pItemPoolTemp;
  2276. pItemPoolTemp = pItemPool;
  2277. while( pItemPoolTemp != NULL )
  2278. {
  2279. // Update the world value
  2280. gWorldItems[ pItemPoolTemp->iItemIndex ].bVisible = HIDDEN_IN_OBJECT;
  2281. pItemPoolTemp->bVisible = HIDDEN_IN_OBJECT;
  2282. pItemPoolTemp = pItemPoolTemp->pNext;
  2283. }
  2284. }
  2285. // This determines the overall initial visibility of the pool...
  2286. // IF ANY are set to VISIBLE, MODIFY
  2287. void AdjustItemPoolVisibility( ITEM_POOL *pItemPool )
  2288. {
  2289. ITEM_POOL *pItemPoolTemp;
  2290. BOOLEAN fAtLeastModified = FALSE;
  2291. pItemPoolTemp = pItemPool;
  2292. while( pItemPoolTemp != NULL )
  2293. {
  2294. // DEFAULT ITEM POOL TO INVISIBLE....
  2295. pItemPoolTemp->bVisible = INVISIBLE;
  2296. // Update each item...
  2297. // If we have reached a visible value we should not modify, ignore...
  2298. if ( gWorldItems[ pItemPoolTemp->iItemIndex ].bVisible == VISIBLE )
  2299. {
  2300. fAtLeastModified = TRUE;
  2301. }
  2302. pItemPoolTemp = pItemPoolTemp->pNext;
  2303. }
  2304. // Handle obscured flag...
  2305. HandleItemObscuredFlag( pItemPool->sGridNo, pItemPool->ubLevel );
  2306. // If we didn;t find any that should be modified..
  2307. if ( !fAtLeastModified )
  2308. {
  2309. return;
  2310. }
  2311. // Update global pool bVisible to true ( if at least one is visible... )
  2312. pItemPoolTemp = pItemPool;
  2313. while( pItemPoolTemp != NULL )
  2314. {
  2315. pItemPoolTemp->bVisible = VISIBLE;
  2316. pItemPoolTemp = pItemPoolTemp->pNext;
  2317. }
  2318. // Handle obscured flag...
  2319. HandleItemObscuredFlag( pItemPool->sGridNo, pItemPool->ubLevel );
  2320. }
  2321. BOOLEAN RemoveItemFromPool( INT16 sGridNo, INT32 iItemIndex, UINT8 ubLevel )
  2322. {
  2323. ITEM_POOL *pItemPool;
  2324. ITEM_POOL *pItemPoolTemp;
  2325. BOOLEAN fItemFound = FALSE;
  2326. LEVELNODE *pObject;
  2327. // Check for and existing pool on the object layer
  2328. if ( GetItemPool( sGridNo, &pItemPool, ubLevel ) )
  2329. {
  2330. // REMOVE FROM LIST
  2331. // LOOP THROUGH LIST TO FIND NODE WE WANT
  2332. pItemPoolTemp = pItemPool;
  2333. while( pItemPoolTemp != NULL )
  2334. {
  2335. if ( pItemPoolTemp->iItemIndex == iItemIndex )
  2336. {
  2337. fItemFound = TRUE;
  2338. break;
  2339. }
  2340. pItemPoolTemp = pItemPoolTemp->pNext;
  2341. }
  2342. if ( !fItemFound )
  2343. {
  2344. // COULDNOT FIND ITEM? MAYBE SOMEBODY GOT IT BEFORE WE GOT THERE!
  2345. return( FALSE );
  2346. }
  2347. // REMOVE GRAPHIC
  2348. RemoveItemGraphicFromWorld( &(Item[ gWorldItems[ iItemIndex ].o.usItem ] ), sGridNo, ubLevel, pItemPoolTemp->pLevelNode );
  2349. // IF WE ARE LOCATIONG STILL, KILL LOCATOR!
  2350. if ( pItemPoolTemp->bFlashColor != 0 )
  2351. {
  2352. // REMOVE TIMER!
  2353. RemoveFlashItemSlot( pItemPoolTemp );
  2354. }
  2355. // REMOVE PREV
  2356. if ( pItemPoolTemp->pPrev != NULL )
  2357. {
  2358. pItemPoolTemp->pPrev->pNext = pItemPoolTemp->pNext;
  2359. }
  2360. // REMOVE NEXT
  2361. if ( pItemPoolTemp->pNext != NULL )
  2362. {
  2363. pItemPoolTemp->pNext->pPrev = pItemPoolTemp->pPrev;
  2364. }
  2365. // IF THIS NODE WAS THE HEAD, SET ANOTHER AS HEAD AT THIS GRIDNO
  2366. if ( pItemPoolTemp->pPrev == NULL )
  2367. {
  2368. // WE'RE HEAD
  2369. if ( ubLevel == 0 )
  2370. {
  2371. pObject = gpWorldLevelData[ sGridNo ].pStructHead;
  2372. }
  2373. else
  2374. {
  2375. pObject = gpWorldLevelData[ sGridNo ].pOnRoofHead;
  2376. }
  2377. fItemFound = FALSE;
  2378. // LOOP THORUGH OBJECT LAYER
  2379. while( pObject != NULL )
  2380. {
  2381. if ( pObject->uiFlags & LEVELNODE_ITEM )
  2382. {
  2383. // ADJUST TO NEXT GUY FOR HEAD
  2384. pObject->pItemPool = pItemPoolTemp->pNext;
  2385. fItemFound = TRUE;
  2386. }
  2387. pObject = pObject->pNext;
  2388. }
  2389. if (!fItemFound)
  2390. {
  2391. // THIS WAS THE LAST ITEM IN THE POOL!
  2392. gpWorldLevelData[sGridNo].uiFlags &= ~(MAPELEMENT_ITEMPOOL_PRESENT);
  2393. }
  2394. }
  2395. // Find any structure with flag set as having items on top.. if this one did...
  2396. if ( pItemPoolTemp->bRenderZHeightAboveLevel > 0 )
  2397. {
  2398. STRUCTURE *pStructure;
  2399. ITEM_POOL *pTempPool;
  2400. // Check if an item pool exists here....
  2401. if ( !GetItemPool( pItemPoolTemp->sGridNo, &pTempPool, pItemPoolTemp->ubLevel ) )
  2402. {
  2403. pStructure = FindStructure( pItemPoolTemp->sGridNo , STRUCTURE_HASITEMONTOP );
  2404. if ( pStructure != NULL )
  2405. {
  2406. // Remove...
  2407. pStructure->fFlags &= (~STRUCTURE_HASITEMONTOP );
  2408. // Re-adjust interactive tile...
  2409. BeginCurInteractiveTileCheck( INTILE_CHECK_SELECTIVE );
  2410. }
  2411. }
  2412. }
  2413. AdjustItemPoolVisibility( pItemPoolTemp );
  2414. // DELETE
  2415. MemFree( pItemPoolTemp );
  2416. RemoveItemFromWorld( iItemIndex );
  2417. return( TRUE );
  2418. }
  2419. return( FALSE );
  2420. }
  2421. BOOLEAN MoveItemPools( INT16 sStartPos, INT16 sEndPos )
  2422. {
  2423. // note, only works between locations on the ground
  2424. ITEM_POOL *pItemPool;
  2425. WORLDITEM TempWorldItem;
  2426. // While there is an existing pool
  2427. while( GetItemPool( sStartPos, &pItemPool, 0 ) )
  2428. {
  2429. memcpy( &TempWorldItem, &(gWorldItems[ pItemPool->iItemIndex ]), sizeof( WORLDITEM ) );
  2430. RemoveItemFromPool( sStartPos, pItemPool->iItemIndex, 0 );
  2431. AddItemToPool( sEndPos, &(TempWorldItem.o), -1, TempWorldItem.ubLevel, TempWorldItem.usFlags, TempWorldItem.bRenderZHeightAboveLevel );
  2432. }
  2433. return( TRUE );
  2434. }
  2435. BOOLEAN GetItemPool( UINT16 usMapPos, ITEM_POOL **ppItemPool, UINT8 ubLevel )
  2436. {
  2437. LEVELNODE *pObject;
  2438. if ( ubLevel == 0 )
  2439. {
  2440. pObject = gpWorldLevelData[ usMapPos ].pStructHead;
  2441. }
  2442. else
  2443. {
  2444. pObject = gpWorldLevelData[ usMapPos ].pOnRoofHead;
  2445. }
  2446. (*ppItemPool) = NULL;
  2447. // LOOP THORUGH OBJECT LAYER
  2448. while( pObject != NULL )
  2449. {
  2450. if ( pObject->uiFlags & LEVELNODE_ITEM )
  2451. {
  2452. (*ppItemPool) = pObject->pItemPool;
  2453. //DEF added the check because pObject->pItemPool was NULL which was causing problems
  2454. if( *ppItemPool )
  2455. return( TRUE );
  2456. else
  2457. return( FALSE );
  2458. }
  2459. pObject = pObject->pNext;
  2460. }
  2461. return( FALSE );
  2462. }
  2463. void NotifySoldiersToLookforItems( )
  2464. {
  2465. UINT32 cnt;
  2466. SOLDIERTYPE *pSoldier;
  2467. for ( cnt = 0; cnt < guiNumMercSlots; cnt++ )
  2468. {
  2469. pSoldier = MercSlots[ cnt ];
  2470. if ( pSoldier != NULL )
  2471. {
  2472. pSoldier->uiStatusFlags |= SOLDIER_LOOKFOR_ITEMS;
  2473. }
  2474. }
  2475. }
  2476. void AllSoldiersLookforItems( BOOLEAN fShowLocators )
  2477. {
  2478. UINT32 cnt;
  2479. SOLDIERTYPE *pSoldier;
  2480. for ( cnt = 0; cnt < guiNumMercSlots; cnt++ )
  2481. {
  2482. pSoldier = MercSlots[ cnt ];
  2483. if ( pSoldier != NULL )
  2484. {
  2485. RevealRoofsAndItems(pSoldier, TRUE, fShowLocators, pSoldier->bLevel, FALSE );
  2486. }
  2487. }
  2488. }
  2489. INT16 GetNumOkForDisplayItemsInPool( ITEM_POOL *pItemPool, INT8 bZLevel )
  2490. {
  2491. INT32 cnt;
  2492. //Determine total #
  2493. cnt = 0;
  2494. while( pItemPool != NULL )
  2495. {
  2496. if ( ItemPoolOKForDisplay( pItemPool, bZLevel ) )
  2497. {
  2498. cnt++;
  2499. }
  2500. pItemPool = pItemPool->pNext;
  2501. }
  2502. return( (UINT16) cnt );
  2503. }
  2504. BOOLEAN AnyItemsVisibleOnLevel( ITEM_POOL *pItemPool, INT8 bZLevel )
  2505. {
  2506. if ( ( gTacticalStatus.uiFlags & SHOW_ALL_ITEMS ) )
  2507. {
  2508. return( TRUE );
  2509. }
  2510. //Determine total #
  2511. while( pItemPool != NULL )
  2512. {
  2513. if ( pItemPool->bRenderZHeightAboveLevel == bZLevel )
  2514. {
  2515. if ( gWorldItems[ pItemPool->iItemIndex ].bVisible == VISIBLE )
  2516. {
  2517. return( TRUE );
  2518. }
  2519. }
  2520. pItemPool = pItemPool->pNext;
  2521. }
  2522. return( FALSE );
  2523. }
  2524. BOOLEAN ItemPoolOKForDisplay( ITEM_POOL *pItemPool, INT8 bZLevel )
  2525. {
  2526. if (gTacticalStatus.uiFlags&SHOW_ALL_ITEMS)
  2527. {
  2528. return( TRUE );
  2529. }
  2530. // Setup some conditions!
  2531. if ( gWorldItems[ pItemPool->iItemIndex ].bVisible != VISIBLE )
  2532. {
  2533. return( FALSE );
  2534. }
  2535. // If -1, it means find all
  2536. if ( pItemPool->bRenderZHeightAboveLevel != bZLevel && bZLevel != -1 )
  2537. {
  2538. return( FALSE );
  2539. }
  2540. return( TRUE );
  2541. }
  2542. BOOLEAN ItemPoolOKForPickup( SOLDIERTYPE * pSoldier, ITEM_POOL *pItemPool, INT8 bZLevel )
  2543. {
  2544. if (gTacticalStatus.uiFlags&SHOW_ALL_ITEMS)
  2545. {
  2546. return( TRUE );
  2547. }
  2548. if ( pSoldier->bTeam == gbPlayerNum )
  2549. {
  2550. // Setup some conditions!
  2551. if ( gWorldItems[ pItemPool->iItemIndex ].bVisible != VISIBLE )
  2552. {
  2553. return( FALSE );
  2554. }
  2555. }
  2556. // If -1, it means find all
  2557. if ( pItemPool->bRenderZHeightAboveLevel != bZLevel && bZLevel != -1 )
  2558. {
  2559. return( FALSE );
  2560. }
  2561. return( TRUE );
  2562. }
  2563. extern void HandleAnyMercInSquadHasCompatibleStuff( UINT8 ubSquad, OBJECTTYPE *pObject, BOOLEAN fReset );
  2564. BOOLEAN DrawItemPoolList( ITEM_POOL *pItemPool, INT16 sGridNo, UINT8 bCommand, INT8 bZLevel, INT16 sXPos, INT16 sYPos )
  2565. {
  2566. INT16 sY;
  2567. INVTYPE *pItem;
  2568. ITEM_POOL *pTempItemPool;
  2569. INT16 pStr[ 100 ];
  2570. INT16 cnt = 0, sHeight = 0;
  2571. INT16 sLargeLineWidth = 0, sLineWidth;
  2572. BOOLEAN fRecalcNumListed = FALSE;
  2573. BOOLEAN fSelectionDone = FALSE;
  2574. INT8 gbCurrentItemSel = 0;
  2575. INT8 bNumItemsListed = 0;
  2576. INT16 sFontX, sFontY;
  2577. INT16 sLargestLineWidth = 30;
  2578. INT8 bCurStart = 0;
  2579. BOOLEAN fDoBack;
  2580. // Take a look at each guy in current sqaud and check for compatible ammo...
  2581. // Determine how many there are
  2582. // MOVE HEAD TO CURRENT START
  2583. cnt = 0;
  2584. pTempItemPool = pItemPool;
  2585. while( pTempItemPool != NULL )
  2586. {
  2587. if ( cnt == bCurStart )
  2588. {
  2589. break;
  2590. }
  2591. // ATE: Put some conditions on this....
  2592. if ( ItemPoolOKForDisplay( pTempItemPool, bZLevel ) )
  2593. {
  2594. cnt++;
  2595. }
  2596. pTempItemPool = pTempItemPool->pNext;
  2597. }
  2598. cnt = bCurStart;
  2599. fDoBack = FALSE;
  2600. while( pTempItemPool != NULL )
  2601. {
  2602. // IF WE HAVE MORE THAN THE SET AMOUNT, QUIT NOW!
  2603. if ( cnt == ( bCurStart + NUM_ITEMS_LISTED ) )
  2604. {
  2605. cnt++;
  2606. fDoBack = TRUE;
  2607. break;
  2608. }
  2609. // ATE: Put some conditions on this....
  2610. if ( ItemPoolOKForDisplay( pTempItemPool, bZLevel ) )
  2611. {
  2612. cnt++;
  2613. }
  2614. sHeight += GetFontHeight( SMALLFONT1 ) - 2;
  2615. pTempItemPool = pTempItemPool->pNext;
  2616. }
  2617. pTempItemPool = pItemPool;
  2618. while( pTempItemPool != NULL )
  2619. {
  2620. // ATE: Put some conditions on this....
  2621. if ( ItemPoolOKForDisplay( pTempItemPool, bZLevel ) )
  2622. {
  2623. HandleAnyMercInSquadHasCompatibleStuff( (INT8)CurrentSquad( ), &(gWorldItems[ pTempItemPool->iItemIndex ].o ), FALSE );
  2624. }
  2625. pTempItemPool = pTempItemPool->pNext;
  2626. }
  2627. // IF COUNT IS ALREADY > MAX, ADD A PREV...
  2628. if ( bCurStart >= NUM_ITEMS_LISTED )
  2629. {
  2630. cnt++;
  2631. }
  2632. bNumItemsListed = (INT8)cnt;
  2633. //RENDER LIST!
  2634. // Determine max length
  2635. pTempItemPool = pItemPool;
  2636. while( pTempItemPool != NULL )
  2637. {
  2638. if ( ItemPoolOKForDisplay( pTempItemPool, bZLevel ) )
  2639. {
  2640. // GET ITEM
  2641. pItem = &Item[ gWorldItems[ pTempItemPool->iItemIndex ].o.usItem ];
  2642. // Set string
  2643. if ( gWorldItems[ pTempItemPool->iItemIndex ].o.ubNumberOfObjects > 1 )
  2644. {
  2645. swprintf( pStr, L"%s (%d)", ShortItemNames[ gWorldItems[ pTempItemPool->iItemIndex ].o.usItem ], gWorldItems[ pTempItemPool->iItemIndex ].o.ubNumberOfObjects );
  2646. }
  2647. else
  2648. {
  2649. swprintf( pStr, L"%s", ShortItemNames[ gWorldItems[ pTempItemPool->iItemIndex ].o.usItem ] );
  2650. }
  2651. // Get Width
  2652. sLineWidth = StringPixLength( pStr,SMALLFONT1 );
  2653. if ( sLineWidth > sLargeLineWidth )
  2654. {
  2655. sLargeLineWidth = sLineWidth;
  2656. }
  2657. sLargestLineWidth = sLargeLineWidth;
  2658. }
  2659. pTempItemPool = pTempItemPool->pNext;
  2660. }
  2661. // Determine where our mouse is!
  2662. if ( sXPos > ( 640 - sLargestLineWidth ) )
  2663. {
  2664. sFontX = sXPos - sLargestLineWidth;
  2665. }
  2666. else
  2667. {
  2668. sFontX = sXPos + 15;
  2669. }
  2670. sFontY = sYPos;
  2671. // Move up if over interface....
  2672. if ( ( sFontY + sHeight ) > 340 )
  2673. {
  2674. sFontY = 340 - sHeight;
  2675. }
  2676. // Detertime vertiacal center
  2677. sFontY -= ( sHeight / 2 );
  2678. SetFont( SMALLFONT1 );
  2679. SetFontBackground( FONT_MCOLOR_BLACK );
  2680. SetFontForeground( FONT_MCOLOR_DKGRAY );
  2681. // MOVE HEAD TO CURRENT START
  2682. cnt = 0;
  2683. while( pItemPool != NULL )
  2684. {
  2685. if ( cnt == bCurStart )
  2686. {
  2687. break;
  2688. }
  2689. if ( ItemPoolOKForDisplay( pItemPool, bZLevel ) )
  2690. {
  2691. cnt++;
  2692. }
  2693. pItemPool = pItemPool->pNext;
  2694. }
  2695. // START DISPLAY LOOP
  2696. cnt = bCurStart;
  2697. sY = sFontY;
  2698. // ADD PREV TO THIS LIST!
  2699. if ( bCurStart >= NUM_ITEMS_LISTED )
  2700. {
  2701. // Set string
  2702. if ( cnt == gbCurrentItemSel )
  2703. {
  2704. SetFontForeground( FONT_MCOLOR_LTGRAY );
  2705. }
  2706. else
  2707. {
  2708. SetFontForeground( FONT_MCOLOR_DKGRAY );
  2709. }
  2710. swprintf( pStr, TacticalStr[ ITEMPOOL_POPUP_PREV_STR ] );
  2711. gprintfdirty( sFontX, sY, pStr );
  2712. mprintf( sFontX, sY, pStr );
  2713. sY += GetFontHeight( SMALLFONT1 ) - 2;
  2714. cnt++;
  2715. }
  2716. while( pItemPool != NULL )
  2717. {
  2718. if ( bCommand == ITEMLIST_HANDLE )
  2719. {
  2720. if ( cnt == gbCurrentItemSel )
  2721. {
  2722. SetFontForeground( FONT_MCOLOR_LTGRAY );
  2723. }
  2724. else
  2725. {
  2726. SetFontForeground( FONT_MCOLOR_DKGRAY );
  2727. }
  2728. }
  2729. if ( ItemPoolOKForDisplay( pItemPool, bZLevel ) )
  2730. {
  2731. // GET ITEM
  2732. pItem = &Item[ gWorldItems[ pItemPool->iItemIndex ].o.usItem ];
  2733. // Set string
  2734. if ( gWorldItems[ pItemPool->iItemIndex ].o.ubNumberOfObjects > 1 )
  2735. {
  2736. swprintf( pStr, L"%s (%d)", ShortItemNames[ gWorldItems[ pItemPool->iItemIndex ].o.usItem ], gWorldItems[ pItemPool->iItemIndex ].o.ubNumberOfObjects );
  2737. }
  2738. else
  2739. {
  2740. swprintf( pStr, L"%s", ShortItemNames[ gWorldItems[ pItemPool->iItemIndex ].o.usItem ] );
  2741. }
  2742. gprintfdirty( sFontX, sY, pStr );
  2743. mprintf( sFontX, sY, pStr );
  2744. sY += GetFontHeight( SMALLFONT1 ) - 2;
  2745. cnt++;
  2746. }
  2747. pItemPool = pItemPool->pNext;
  2748. if ( fDoBack )
  2749. {
  2750. if ( cnt == ( bNumItemsListed - 1) )
  2751. {
  2752. break;
  2753. }
  2754. }
  2755. }
  2756. if ( fDoBack )
  2757. {
  2758. if ( cnt == ( bNumItemsListed - 1) )
  2759. {
  2760. // Set string
  2761. if ( cnt == gbCurrentItemSel )
  2762. {
  2763. SetFontForeground( FONT_MCOLOR_LTGRAY );
  2764. }
  2765. else
  2766. {
  2767. SetFontForeground( FONT_MCOLOR_DKGRAY );
  2768. }
  2769. swprintf( pStr, TacticalStr[ ITEMPOOL_POPUP_MORE_STR ] );
  2770. gprintfdirty( sFontX, sY, pStr );
  2771. mprintf( sFontX, sY, pStr );
  2772. }
  2773. }
  2774. return( fSelectionDone );
  2775. }
  2776. INT8 GetListMouseHotSpot( INT16 sLargestLineWidth, INT8 bNumItemsListed, INT16 sFontX, INT16 sFontY, INT8 bCurStart )
  2777. {
  2778. INT16 cnt = 0;
  2779. INT16 sTestX1, sTestX2, sTestY1, sTestY2;
  2780. INT16 sLineHeight;
  2781. INT8 gbCurrentItemSel = -1;
  2782. INT8 bListedItems;
  2783. sLineHeight = GetFontHeight( SMALLFONT1 ) - 2;
  2784. sTestX1 = sFontX;
  2785. sTestX2 = sFontX + sLargestLineWidth;
  2786. bListedItems = ( bNumItemsListed - bCurStart );
  2787. if ( gusMouseXPos < sTestX1 || gusMouseXPos > sTestX2 )
  2788. {
  2789. gbCurrentItemSel = -1;
  2790. }
  2791. else
  2792. {
  2793. // Determine where mouse is!
  2794. for ( cnt = 0; cnt < bListedItems; cnt++ )
  2795. {
  2796. sTestY1 = sFontY + ( sLineHeight * cnt );
  2797. sTestY2 = sFontY + ( sLineHeight * ( cnt + 1 ) );
  2798. if ( gusMouseYPos > sTestY1 && gusMouseYPos < sTestY2 )
  2799. {
  2800. gbCurrentItemSel = (INT8)cnt;
  2801. break;
  2802. }
  2803. }
  2804. }
  2805. // OFFSET START
  2806. gbCurrentItemSel += bCurStart;
  2807. return( gbCurrentItemSel );
  2808. }
  2809. void SetItemPoolLocator( ITEM_POOL *pItemPool )
  2810. {
  2811. pItemPool->bFlashColor = 59;
  2812. pItemPool->uiTimerID = AddFlashItemSlot( pItemPool, NULL, 0 );
  2813. }
  2814. void SetItemPoolLocatorWithCallback( ITEM_POOL *pItemPool, ITEM_POOL_LOCATOR_HOOK Callback )
  2815. {
  2816. pItemPool->bFlashColor = 59;
  2817. pItemPool->uiTimerID = AddFlashItemSlot( pItemPool, Callback, 0 );
  2818. }
  2819. /// ITEM POOL INDICATOR FUNCTIONS
  2820. INT32 GetFreeFlashItemSlot(void)
  2821. {
  2822. UINT32 uiCount;
  2823. for(uiCount=0; uiCount < guiNumFlashItemSlots; uiCount++)
  2824. {
  2825. if(( FlashItemSlots[uiCount].fAllocated == FALSE ) )
  2826. return((INT32)uiCount);
  2827. }
  2828. if(guiNumFlashItemSlots < NUM_ITEM_FLASH_SLOTS )
  2829. return((INT32)guiNumFlashItemSlots++);
  2830. return(-1);
  2831. }
  2832. void RecountFlashItemSlots(void)
  2833. {
  2834. INT32 uiCount;
  2835. for(uiCount=guiNumFlashItemSlots-1; (uiCount >=0) ; uiCount--)
  2836. {
  2837. if( ( FlashItemSlots[uiCount].fAllocated ) )
  2838. {
  2839. guiNumFlashItemSlots=(UINT32)(uiCount+1);
  2840. break;
  2841. }
  2842. }
  2843. }
  2844. INT32 AddFlashItemSlot( ITEM_POOL *pItemPool, ITEM_POOL_LOCATOR_HOOK Callback, UINT8 ubFlags )
  2845. {
  2846. INT32 iFlashItemIndex;
  2847. if( ( iFlashItemIndex = GetFreeFlashItemSlot() )==(-1) )
  2848. return(-1);
  2849. ubFlags |= ITEM_LOCATOR_LOCKED;
  2850. FlashItemSlots[ iFlashItemIndex ].pItemPool = pItemPool;
  2851. FlashItemSlots[ iFlashItemIndex ].bRadioFrame = 0;
  2852. FlashItemSlots[ iFlashItemIndex ].uiLastFrameUpdate = GetJA2Clock( );
  2853. FlashItemSlots[ iFlashItemIndex ].Callback = Callback;
  2854. FlashItemSlots[ iFlashItemIndex ].fAllocated = TRUE;
  2855. FlashItemSlots[ iFlashItemIndex ].ubFlags = ubFlags;
  2856. return( iFlashItemIndex );
  2857. }
  2858. BOOLEAN RemoveFlashItemSlot( ITEM_POOL *pItemPool )
  2859. {
  2860. UINT32 uiCount;
  2861. CHECKF( pItemPool != NULL );
  2862. for( uiCount=0; uiCount < guiNumFlashItemSlots; uiCount++)
  2863. {
  2864. if ( FlashItemSlots[ uiCount ].fAllocated )
  2865. {
  2866. if ( FlashItemSlots[ uiCount ].pItemPool == pItemPool )
  2867. {
  2868. FlashItemSlots[ uiCount ].fAllocated = FALSE;
  2869. // Check if we have a callback and call it if so!
  2870. if ( FlashItemSlots[ uiCount ].Callback != NULL )
  2871. {
  2872. FlashItemSlots[ uiCount ].Callback( );
  2873. }
  2874. return( TRUE );
  2875. }
  2876. }
  2877. }
  2878. return( TRUE );
  2879. }
  2880. void HandleFlashingItems( )
  2881. {
  2882. UINT32 cnt;
  2883. ITEM_POOL *pItemPool;
  2884. LEVELNODE *pObject;
  2885. ITEM_POOL_LOCATOR *pLocator;
  2886. BOOLEAN fDoLocator = FALSE;
  2887. if ( COUNTERDONE( CYCLERENDERITEMCOLOR ) )
  2888. {
  2889. RESETCOUNTER( CYCLERENDERITEMCOLOR );
  2890. for ( cnt = 0; cnt < guiNumFlashItemSlots; cnt++ )
  2891. {
  2892. pLocator = &( FlashItemSlots[ cnt ] );
  2893. if ( pLocator->fAllocated )
  2894. {
  2895. fDoLocator = TRUE;
  2896. if ( ( pLocator->ubFlags & ITEM_LOCATOR_LOCKED ) )
  2897. {
  2898. if ( gTacticalStatus.fLockItemLocators == FALSE )
  2899. {
  2900. // Turn off!
  2901. pLocator->ubFlags &= (~ITEM_LOCATOR_LOCKED);
  2902. }
  2903. else
  2904. {
  2905. fDoLocator = FALSE;
  2906. }
  2907. }
  2908. if ( fDoLocator )
  2909. {
  2910. pItemPool = pLocator->pItemPool;
  2911. // Update radio locator
  2912. {
  2913. UINT32 uiClock;
  2914. uiClock = GetJA2Clock( );
  2915. // Update frame values!
  2916. if ( ( uiClock - pLocator->uiLastFrameUpdate ) > 80 )
  2917. {
  2918. pLocator->uiLastFrameUpdate = uiClock;
  2919. // Update frame
  2920. pLocator->bRadioFrame++;
  2921. if ( pLocator->bRadioFrame == 5 )
  2922. {
  2923. pLocator->bRadioFrame = 0;
  2924. }
  2925. }
  2926. }
  2927. // UPDATE FLASH COLOR VALUE
  2928. pItemPool->bFlashColor--;
  2929. if ( pItemPool->ubLevel == 0 )
  2930. {
  2931. pObject = gpWorldLevelData[ pItemPool->sGridNo ].pStructHead;
  2932. }
  2933. else
  2934. {
  2935. pObject = gpWorldLevelData[ pItemPool->sGridNo ].pOnRoofHead;
  2936. }
  2937. // LOOP THORUGH OBJECT LAYER
  2938. while( pObject != NULL )
  2939. {
  2940. if ( pObject->uiFlags & LEVELNODE_ITEM )
  2941. {
  2942. if ( pItemPool->bFlashColor == 1 )
  2943. {
  2944. //pObject->uiFlags &= (~LEVELNODE_DYNAMIC);
  2945. //pObject->uiFlags |= ( LEVELNODE_LASTDYNAMIC );
  2946. }
  2947. else
  2948. {
  2949. //pObject->uiFlags |= LEVELNODE_DYNAMIC;
  2950. }
  2951. }
  2952. pObject = pObject->pNext;
  2953. }
  2954. if ( pItemPool->bFlashColor == 1 )
  2955. {
  2956. pItemPool->bFlashColor = 0;
  2957. // REMOVE TIMER!
  2958. RemoveFlashItemSlot( pItemPool );
  2959. SetRenderFlags( RENDER_FLAG_FULL );
  2960. }
  2961. }
  2962. }
  2963. }
  2964. RecountFlashItemSlots( );
  2965. }
  2966. }
  2967. void RenderTopmostFlashingItems( )
  2968. {
  2969. UINT32 cnt;
  2970. ITEM_POOL *pItemPool;
  2971. ITEM_POOL_LOCATOR *pLocator;
  2972. for ( cnt = 0; cnt < guiNumFlashItemSlots; cnt++ )
  2973. {
  2974. pLocator = &( FlashItemSlots[ cnt ] );
  2975. if ( pLocator->fAllocated )
  2976. {
  2977. if ( !( pLocator->ubFlags & ( ITEM_LOCATOR_LOCKED ) ) )
  2978. {
  2979. pItemPool = pLocator->pItemPool;
  2980. // Update radio locator
  2981. {
  2982. FLOAT dOffsetX, dOffsetY;
  2983. FLOAT dTempX_S, dTempY_S;
  2984. INT16 sX, sY, sXPos, sYPos;
  2985. INT32 iBack;
  2986. ConvertGridNoToCenterCellXY( pItemPool->sGridNo, &sX, &sY );
  2987. dOffsetX = (FLOAT)( sX - gsRenderCenterX );
  2988. dOffsetY = (FLOAT)( sY - gsRenderCenterY );
  2989. // Calculate guy's position
  2990. FloatFromCellToScreenCoordinates( dOffsetX, dOffsetY, &dTempX_S, &dTempY_S );
  2991. sXPos = ( ( gsVIEWPORT_END_X - gsVIEWPORT_START_X ) /2 ) + (INT16)dTempX_S;
  2992. sYPos = ( ( gsVIEWPORT_END_Y - gsVIEWPORT_START_Y ) /2 ) + (INT16)dTempY_S - gpWorldLevelData[ pItemPool->sGridNo ].sHeight;
  2993. // Adjust for offset position on screen
  2994. sXPos -= gsRenderWorldOffsetX;
  2995. sYPos -= gsRenderWorldOffsetY;
  2996. sYPos -= pItemPool->bRenderZHeightAboveLevel;
  2997. // Adjust for render height
  2998. sYPos += gsRenderHeight;
  2999. // Adjust for level height
  3000. if ( pItemPool->ubLevel )
  3001. {
  3002. sYPos -= ROOF_LEVEL_HEIGHT;
  3003. }
  3004. // Center circle!
  3005. sXPos -= 20;
  3006. sYPos -= 20;
  3007. iBack = RegisterBackgroundRect( BGND_FLAG_SINGLE, NULL, sXPos, sYPos, (INT16)(sXPos +40 ), (INT16)(sYPos + 40 ) );
  3008. if ( iBack != -1 )
  3009. {
  3010. SetBackgroundRectFilled( iBack );
  3011. }
  3012. BltVideoObjectFromIndex( FRAME_BUFFER, guiRADIO, pLocator->bRadioFrame, sXPos, sYPos, VO_BLT_SRCTRANSPARENCY, NULL );
  3013. DrawItemPoolList( pItemPool, pItemPool->sGridNo , ITEMLIST_DISPLAY, pItemPool->bRenderZHeightAboveLevel, sXPos, sYPos );
  3014. }
  3015. }
  3016. }
  3017. }
  3018. }
  3019. BOOLEAN VerifyGiveItem( SOLDIERTYPE *pSoldier, SOLDIERTYPE **ppTargetSoldier )
  3020. {
  3021. SOLDIERTYPE *pTSoldier;
  3022. UINT16 usSoldierIndex;
  3023. OBJECTTYPE *pObject;
  3024. INT16 sGridNo;
  3025. UINT8 ubDirection;
  3026. UINT8 ubTargetMercID;
  3027. // DO SOME CHECKS IF WE CAN DO ANIMATION.....
  3028. // Get items from pending data
  3029. pObject = pSoldier->pTempObject;
  3030. sGridNo = pSoldier->sPendingActionData2;
  3031. ubDirection = pSoldier->bPendingActionData3;
  3032. ubTargetMercID = (UINT8)pSoldier->uiPendingActionData4;
  3033. usSoldierIndex = WhoIsThere2( sGridNo, pSoldier->bLevel );
  3034. // See if our target is still available
  3035. if ( usSoldierIndex != NOBODY )
  3036. {
  3037. // Check if it's the same merc!
  3038. if ( usSoldierIndex != ubTargetMercID )
  3039. {
  3040. return( FALSE );
  3041. }
  3042. // Get soldier
  3043. GetSoldier( &pTSoldier, usSoldierIndex );
  3044. // Look for item in hand....
  3045. (*ppTargetSoldier) = pTSoldier;
  3046. return( TRUE );
  3047. }
  3048. else
  3049. {
  3050. if ( pSoldier->pTempObject != NULL )
  3051. {
  3052. AddItemToPool( pSoldier->sGridNo, pSoldier->pTempObject, 1, pSoldier->bLevel, 0 , -1 );
  3053. // Place it on the ground!
  3054. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, TacticalStr[ ITEM_HAS_BEEN_PLACED_ON_GROUND_STR ], ShortItemNames[ pSoldier->pTempObject->usItem ] );
  3055. // OK, disengage buddy
  3056. pSoldier->uiStatusFlags &= (~SOLDIER_ENGAGEDINACTION );
  3057. if ( ubTargetMercID != NOBODY )
  3058. {
  3059. MercPtrs[ ubTargetMercID ]->uiStatusFlags &= (~SOLDIER_ENGAGEDINACTION );
  3060. }
  3061. MemFree( pSoldier->pTempObject );
  3062. pSoldier->pTempObject = NULL;
  3063. }
  3064. }
  3065. return( FALSE );
  3066. }
  3067. void SoldierGiveItemFromAnimation( SOLDIERTYPE *pSoldier )
  3068. {
  3069. SOLDIERTYPE *pTSoldier;
  3070. INT8 bInvPos;
  3071. OBJECTTYPE TempObject;
  3072. UINT8 ubProfile;
  3073. INT16 sGridNo;
  3074. UINT8 ubDirection;
  3075. UINT8 ubTargetMercID;
  3076. UINT16 usItemNum;
  3077. BOOLEAN fToTargetPlayer = FALSE;
  3078. // Get items from pending data
  3079. // Get objectype and delete
  3080. memcpy( &TempObject, pSoldier->pTempObject, sizeof( OBJECTTYPE ) );
  3081. MemFree( pSoldier->pTempObject );
  3082. pSoldier->pTempObject = NULL;
  3083. bInvPos = pSoldier->bPendingActionData5;
  3084. usItemNum = TempObject.usItem;
  3085. // ATE: OK, check if we have an item in the cursor from
  3086. // this soldier and from this inv slot, if so, delete!!!!!!!
  3087. if ( gpItemPointer != NULL )
  3088. {
  3089. if ( pSoldier->ubID == gpItemPointerSoldier->ubID )
  3090. {
  3091. if ( bInvPos == gbItemPointerSrcSlot && usItemNum == gpItemPointer->usItem )
  3092. {
  3093. // Remove!!!
  3094. EndItemPointer( );
  3095. }
  3096. }
  3097. }
  3098. sGridNo = pSoldier->sPendingActionData2;
  3099. ubDirection = pSoldier->bPendingActionData3;
  3100. ubTargetMercID = (UINT8)pSoldier->uiPendingActionData4;
  3101. // ATE: Deduct APs!
  3102. DeductPoints( pSoldier, AP_PICKUP_ITEM, 0 );
  3103. if ( VerifyGiveItem( pSoldier, &pTSoldier ) )
  3104. {
  3105. // DAVE! - put stuff here to bring up shopkeeper.......
  3106. //if the user just clicked on an arms dealer
  3107. if( IsMercADealer( pTSoldier->ubProfile ) )
  3108. {
  3109. UnSetEngagedInConvFromPCAction( pSoldier );
  3110. //if the dealer is Micky,
  3111. /*
  3112. if( pTSoldier->ubProfile == MICKY )
  3113. {
  3114. //and the items are alcohol, dont enter the shopkeeper
  3115. if( GetArmsDealerItemTypeFromItemNumber( TempObject.usItem ) == ARMS_DEALER_ALCOHOL )
  3116. return;
  3117. }
  3118. */
  3119. if ( NPCHasUnusedRecordWithGivenApproach( pTSoldier->ubProfile, APPROACH_BUYSELL ) )
  3120. {
  3121. TriggerNPCWithGivenApproach( pTSoldier->ubProfile, APPROACH_BUYSELL, TRUE );
  3122. return;
  3123. }
  3124. // now also check for buy/sell lines (Oct 13)
  3125. /*
  3126. else if ( NPCWillingToAcceptItem( pTSoldier->ubProfile, pSoldier->ubProfile, &TempObject ) )
  3127. {
  3128. TriggerNPCWithGivenApproach( pTSoldier->ubProfile, APPROACH_GIVINGITEM, TRUE );
  3129. return;
  3130. }*/
  3131. else if ( !NPCWillingToAcceptItem( pTSoldier->ubProfile, pSoldier->ubProfile, &TempObject ) )
  3132. {
  3133. //Enter the shopkeeper interface
  3134. EnterShopKeeperInterfaceScreen( pTSoldier->ubProfile );
  3135. // removed the if, because if the player picked up an item straight from the ground or money strait from the money
  3136. // interface, the item would NOT have a bInvPos, therefore it would not get added to the dealer, and would get deleted
  3137. // if ( bInvPos != NO_SLOT )
  3138. {
  3139. // MUST send in NO_SLOT, as the SKI wille expect it to exist in inv if not....
  3140. AddItemToPlayersOfferAreaAfterShopKeeperOpen( &TempObject, NO_SLOT );
  3141. /*
  3142. Changed because if the player gave 1 item from a pile, the rest of the items in the piule would disappear
  3143. // OK, r emove the item, as the SKI will give it back once done
  3144. DeleteObj( &( pSoldier->inv[ bInvPos ] ) );
  3145. */
  3146. if ( bInvPos != NO_SLOT )
  3147. {
  3148. RemoveObjFrom( &pSoldier->inv[ bInvPos ], TempObject.ubNumberOfObjects );
  3149. }
  3150. DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 );
  3151. }
  3152. return;
  3153. }
  3154. }
  3155. // OK< FOR NOW HANDLE NPC's DIFFERENT!
  3156. ubProfile = pTSoldier->ubProfile;
  3157. // 1 ) PLayer to NPC = NPC
  3158. // 2 ) Player to player = player;
  3159. // 3 ) NPC to player = player;
  3160. // 4 ) NPC TO NPC = NPC
  3161. // Switch on target...
  3162. // Are we a player dude.. ( target? )
  3163. if ( ubProfile < FIRST_RPC || RPC_RECRUITED( pTSoldier ) )
  3164. {
  3165. fToTargetPlayer = TRUE;
  3166. }
  3167. if ( fToTargetPlayer )
  3168. {
  3169. // begin giving
  3170. DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 );
  3171. // We are a merc, add!
  3172. if ( !AutoPlaceObject( pTSoldier, &TempObject, TRUE ) )
  3173. {
  3174. // Erase!
  3175. if ( bInvPos != NO_SLOT )
  3176. {
  3177. DeleteObj( &( pSoldier->inv[ bInvPos ] ) );
  3178. DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 );
  3179. }
  3180. AddItemToPool( pSoldier->sGridNo, &TempObject, 1, pSoldier->bLevel, 0 , -1 );
  3181. // We could not place it!
  3182. // Drop it on the ground?
  3183. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, TacticalStr[ ITEM_HAS_BEEN_PLACED_ON_GROUND_STR ], ShortItemNames[ usItemNum ] );
  3184. // OK, disengage buddy
  3185. pSoldier->uiStatusFlags &= (~SOLDIER_ENGAGEDINACTION );
  3186. pTSoldier->uiStatusFlags &= (~SOLDIER_ENGAGEDINACTION );
  3187. }
  3188. else
  3189. {
  3190. // Erase!
  3191. if ( bInvPos != NO_SLOT )
  3192. {
  3193. DeleteObj( &( pSoldier->inv[ bInvPos ] ) );
  3194. DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 );
  3195. }
  3196. // OK, it's given, display message!
  3197. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, TacticalStr[ ITEM_HAS_BEEN_GIVEN_TO_STR ], ShortItemNames[ usItemNum ], pTSoldier->name );
  3198. if (usItemNum == MONEY)
  3199. {
  3200. // are we giving money to an NPC, to whom we owe money?
  3201. if (pTSoldier->ubProfile != NO_PROFILE && gMercProfiles[pTSoldier->ubProfile].iBalance < 0)
  3202. {
  3203. gMercProfiles[pTSoldier->ubProfile].iBalance += TempObject.uiMoneyAmount;
  3204. if (gMercProfiles[pTSoldier->ubProfile].iBalance >= 0)
  3205. {
  3206. // don't let the player accumulate credit (?)
  3207. gMercProfiles[pTSoldier->ubProfile].iBalance = 0;
  3208. // report the payment and set facts to indicate people not being owed money
  3209. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, TacticalStr[ GUY_HAS_BEEN_PAID_IN_FULL_STR ], pTSoldier->name );
  3210. }
  3211. else
  3212. {
  3213. // report the payment
  3214. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, TacticalStr[ GUY_STILL_OWED_STR ], pTSoldier->name, -gMercProfiles[pTSoldier->ubProfile].iBalance );
  3215. }
  3216. }
  3217. }
  3218. }
  3219. }
  3220. else
  3221. {
  3222. // Erase!
  3223. if ( bInvPos != NO_SLOT )
  3224. {
  3225. RemoveObjs( &(pSoldier->inv[ bInvPos ]), TempObject.ubNumberOfObjects );
  3226. DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 );
  3227. }
  3228. // Now intiate conv
  3229. InitiateConversation( pTSoldier, pSoldier, APPROACH_GIVINGITEM, (INT32)&TempObject );
  3230. }
  3231. }
  3232. // OK, disengage buddy
  3233. pSoldier->uiStatusFlags &= (~SOLDIER_ENGAGEDINACTION );
  3234. pTSoldier->uiStatusFlags &= (~SOLDIER_ENGAGEDINACTION );
  3235. }
  3236. INT16 AdjustGridNoForItemPlacement( SOLDIERTYPE *pSoldier, INT16 sGridNo )
  3237. {
  3238. STRUCTURE *pStructure;
  3239. INT16 sDesiredLevel;
  3240. INT16 sActionGridNo;
  3241. BOOLEAN fStructFound = FALSE;
  3242. UINT8 ubDirection;
  3243. INT16 sAdjustedGridNo;
  3244. UINT8 ubTargetID;
  3245. sActionGridNo = sGridNo;
  3246. // Check structure database
  3247. if( gpWorldLevelData[ sGridNo ].pStructureHead )
  3248. {
  3249. // Something is here, check obstruction in future
  3250. sDesiredLevel = pSoldier->bLevel ? STRUCTURE_ON_ROOF : STRUCTURE_ON_GROUND;
  3251. pStructure = FindStructure( (INT16)sGridNo, STRUCTURE_BLOCKSMOVES );
  3252. while( pStructure )
  3253. {
  3254. if( !(pStructure->fFlags & STRUCTURE_PASSABLE) && pStructure->sCubeOffset == sDesiredLevel )
  3255. {
  3256. // Check for openable flag....
  3257. //if ( pStructure->fFlags & ( STRUCTURE_OPENABLE | STRUCTURE_HASITEMONTOP ) )
  3258. {
  3259. fStructFound = TRUE;
  3260. break;
  3261. }
  3262. }
  3263. pStructure = FindNextStructure( pStructure, STRUCTURE_BLOCKSMOVES );
  3264. }
  3265. }
  3266. // ATE: IF a person is found, use adjacent gridno for it!
  3267. ubTargetID = WhoIsThere2( sGridNo, pSoldier->bLevel );
  3268. if ( fStructFound || ( ubTargetID != NOBODY && ubTargetID != pSoldier->ubID ) )
  3269. {
  3270. // GET ADJACENT GRIDNO
  3271. sActionGridNo = FindAdjacentGridEx( pSoldier, sGridNo, &ubDirection, &sAdjustedGridNo, FALSE, FALSE );
  3272. if ( sActionGridNo == -1 )
  3273. {
  3274. sActionGridNo = sAdjustedGridNo;
  3275. }
  3276. }
  3277. return( sActionGridNo );
  3278. }
  3279. void StartBombMessageBox( SOLDIERTYPE * pSoldier, INT16 sGridNo )
  3280. {
  3281. UINT8 ubRoom;
  3282. gpTempSoldier = pSoldier;
  3283. gsTempGridno = sGridNo;
  3284. if (pSoldier->inv[HANDPOS].usItem == REMOTEBOMBTRIGGER)
  3285. {
  3286. DoMessageBox( MSG_BOX_BASIC_SMALL_BUTTONS, TacticalStr[ CHOOSE_BOMB_FREQUENCY_STR ], GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_FOUR_NUMBERED_BUTTONS, BombMessageBoxCallBack, NULL );
  3287. }
  3288. else if (pSoldier->inv[HANDPOS].usItem == REMOTETRIGGER)
  3289. {
  3290. #ifdef JA2DEMO
  3291. {
  3292. UINT8 ubRoom;
  3293. if ( InARoom( pSoldier->sGridNo, &ubRoom ) && ubRoom == 31 )
  3294. {
  3295. SetOffBombsByFrequency( pSoldier->ubID, FIRST_MAP_PLACED_FREQUENCY + 4 );
  3296. DoMercBattleSound( pSoldier, BATTLE_SOUND_OK1 );
  3297. }
  3298. else
  3299. {
  3300. DoMercBattleSound( pSoldier, BATTLE_SOUND_CURSE1 );
  3301. }
  3302. }
  3303. #else
  3304. // ATE ignore the commented-out code and add stuff to open the secret passage here
  3305. /*
  3306. switch( pSoldier->inv[HANDPOS].ubLocationID )
  3307. {
  3308. // check to make sure the appropriate sector is loaded
  3309. }
  3310. SetOffBombsByFrequency( pSoldier->ubID, pSoldier->inv[HANDPOS].bFrequency );
  3311. */
  3312. // PLay sound....
  3313. PlayJA2Sample( USE_STATUE_REMOTE, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
  3314. // Check what sector we are in....
  3315. if ( gWorldSectorX == 3 && gWorldSectorY == MAP_ROW_O && gbWorldSectorZ == 0 )
  3316. {
  3317. if ( InARoom( pSoldier->sGridNo, &ubRoom ) && ubRoom == 4 )
  3318. {
  3319. DoMercBattleSound( pSoldier, BATTLE_SOUND_OK1 );
  3320. // Open statue
  3321. ChangeO3SectorStatue( FALSE );
  3322. }
  3323. else
  3324. {
  3325. DoMercBattleSound( pSoldier, BATTLE_SOUND_CURSE1 );
  3326. }
  3327. }
  3328. else
  3329. {
  3330. DoMercBattleSound( pSoldier, BATTLE_SOUND_CURSE1 );
  3331. }
  3332. #endif
  3333. }
  3334. else if ( FindAttachment( &(pSoldier->inv[HANDPOS]), DETONATOR) != ITEM_NOT_FOUND )
  3335. {
  3336. DoMessageBox( MSG_BOX_BASIC_SMALL_BUTTONS, TacticalStr[ CHOOSE_TIMER_STR ], GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_FOUR_NUMBERED_BUTTONS, BombMessageBoxCallBack, NULL );
  3337. }
  3338. else if ( FindAttachment( &(pSoldier->inv[HANDPOS]), REMDETONATOR) != ITEM_NOT_FOUND )
  3339. {
  3340. DoMessageBox( MSG_BOX_BASIC_SMALL_BUTTONS, TacticalStr[ CHOOSE_REMOTE_FREQUENCY_STR ], GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_FOUR_NUMBERED_BUTTONS, BombMessageBoxCallBack, NULL );
  3341. }
  3342. }
  3343. void BombMessageBoxCallBack( UINT8 ubExitValue )
  3344. {
  3345. if (gpTempSoldier)
  3346. {
  3347. if (gpTempSoldier->inv[HANDPOS].usItem == REMOTEBOMBTRIGGER)
  3348. {
  3349. SetOffBombsByFrequency( gpTempSoldier->ubID, ubExitValue );
  3350. }
  3351. else
  3352. {
  3353. INT32 iResult;
  3354. if (FindAttachment( &(gpTempSoldier->inv[HANDPOS]), REMDETONATOR ) != ITEM_NOT_FOUND )
  3355. {
  3356. iResult = SkillCheck( gpTempSoldier, PLANTING_REMOTE_BOMB_CHECK, 0 );
  3357. }
  3358. else
  3359. {
  3360. iResult = SkillCheck( gpTempSoldier, PLANTING_BOMB_CHECK, 0 );
  3361. }
  3362. if ( iResult >= 0 )
  3363. {
  3364. // EXPLOSIVES GAIN (25): Place a bomb, or buried and armed a mine
  3365. StatChange( gpTempSoldier, EXPLODEAMT, 25, FALSE );
  3366. }
  3367. else
  3368. {
  3369. // EXPLOSIVES GAIN (10): Failed to place a bomb, or bury and arm a mine
  3370. StatChange( gpTempSoldier, EXPLODEAMT, 10, FROM_FAILURE );
  3371. // oops! How badly did we screw up?
  3372. if ( iResult >= -20 )
  3373. {
  3374. // messed up the setting
  3375. if ( ubExitValue == 0 )
  3376. {
  3377. ubExitValue = 1;
  3378. }
  3379. else
  3380. {
  3381. // change up/down by 1
  3382. ubExitValue = (UINT8) (ubExitValue + Random( 3 ) - 1);
  3383. }
  3384. // and continue
  3385. }
  3386. else
  3387. {
  3388. // OOPS! ... BOOM!
  3389. IgniteExplosion( NOBODY, gpTempSoldier->sX, gpTempSoldier->sY, (INT16) (gpWorldLevelData[gpTempSoldier->sGridNo].sHeight), gpTempSoldier->sGridNo, gpTempSoldier->inv[ HANDPOS ].usItem, gpTempSoldier->bLevel );
  3390. return;
  3391. }
  3392. }
  3393. if ( ArmBomb( &(gpTempSoldier->inv[HANDPOS]), ubExitValue ) )
  3394. {
  3395. gpTempSoldier->inv[ HANDPOS ].bTrap = __min( 10, ( EffectiveExplosive( gpTempSoldier ) / 20) + (EffectiveExpLevel( gpTempSoldier ) / 3) );
  3396. // HACK IMMINENT!
  3397. // value of 1 is stored in maps for SIDE of bomb owner... when we want to use IDs!
  3398. // so we add 2 to all owner IDs passed through here and subtract 2 later
  3399. gpTempSoldier->inv[HANDPOS].ubBombOwner = gpTempSoldier->ubID + 2;
  3400. AddItemToPool( gsTempGridno, &(gpTempSoldier->inv[HANDPOS]), 1, gpTempSoldier->bLevel, WORLD_ITEM_ARMED_BOMB, 0 );
  3401. DeleteObj( &(gpTempSoldier->inv[HANDPOS]) );
  3402. }
  3403. }
  3404. }
  3405. }
  3406. BOOLEAN HandItemWorks( SOLDIERTYPE *pSoldier, INT8 bSlot )
  3407. {
  3408. BOOLEAN fItemJustBroke = FALSE, fItemWorks = TRUE;
  3409. OBJECTTYPE * pObj;
  3410. pObj = &( pSoldier->inv[ bSlot ] );
  3411. // if the item can be damaged, than we must check that it's in good enough
  3412. // shape to be usable, and doesn't break during use.
  3413. // Exception: land mines. You can bury them broken, they just won't blow!
  3414. if ( (Item[ pObj->usItem ].fFlags & ITEM_DAMAGEABLE) && (pObj->usItem != MINE) && (Item[ pObj->usItem ].usItemClass != IC_MEDKIT) && pObj->usItem != GAS_CAN )
  3415. {
  3416. // if it's still usable, check whether it breaks
  3417. if ( pObj->bStatus[0] >= USABLE)
  3418. {
  3419. // if a dice roll is greater than the item's status
  3420. if ( (Random(80) + 20) >= (UINT32) (pObj->bStatus[0] + 50) )
  3421. {
  3422. fItemJustBroke = TRUE;
  3423. fItemWorks = FALSE;
  3424. // item breaks, and becomes unusable... so its status is reduced
  3425. // to somewhere between 1 and the 1 less than USABLE
  3426. pObj->bStatus[0] = (INT8) ( 1 + Random( USABLE - 1 ) );
  3427. }
  3428. }
  3429. else // it's already unusable
  3430. {
  3431. fItemWorks = FALSE;
  3432. }
  3433. if (!fItemWorks && pSoldier->bTeam == gbPlayerNum)
  3434. {
  3435. // merc says "This thing doesn't work!"
  3436. TacticalCharacterDialogue( pSoldier, QUOTE_USELESS_ITEM );
  3437. if (fItemJustBroke)
  3438. {
  3439. DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 );
  3440. }
  3441. }
  3442. }
  3443. if ( fItemWorks && bSlot == HANDPOS && Item[ pObj->usItem ].usItemClass == IC_GUN )
  3444. {
  3445. // are we using two guns at once?
  3446. if ( Item[ pSoldier->inv[SECONDHANDPOS].usItem ].usItemClass == IC_GUN &&
  3447. pSoldier->inv[SECONDHANDPOS].bGunStatus >= USABLE &&
  3448. pSoldier->inv[SECONDHANDPOS].ubGunShotsLeft > 0)
  3449. {
  3450. // check the second gun for breakage, and if IT breaks, return false
  3451. return( HandItemWorks( pSoldier, SECONDHANDPOS ) );
  3452. }
  3453. }
  3454. return( fItemWorks );
  3455. }
  3456. void SetOffBoobyTrapInMapScreen( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObject )
  3457. {
  3458. UINT8 ubPtsDmg = 0;
  3459. // check if trapped item is an explosive, if so then up the amount of dmg
  3460. if( ( pObject -> usItem == TNT )|| ( pObject -> usItem == RDX ) )
  3461. {
  3462. // for explosive
  3463. ubPtsDmg = 0;
  3464. }
  3465. else
  3466. {
  3467. // normal mini grenade dmg
  3468. ubPtsDmg = 0;
  3469. }
  3470. // injure the inventory character
  3471. SoldierTakeDamage( pSoldier, 0, ubPtsDmg, ubPtsDmg, TAKE_DAMAGE_EXPLOSION, NOBODY, NOWHERE, 0, TRUE );
  3472. // play the sound
  3473. PlayJA2Sample( EXPLOSION_1, RATE_11025, BTNVOLUME, 1, MIDDLEPAN );
  3474. }
  3475. void SetOffBoobyTrap( ITEM_POOL * pItemPool )
  3476. {
  3477. if ( pItemPool )
  3478. {
  3479. INT16 sX, sY;
  3480. sX = CenterX( pItemPool->sGridNo );
  3481. sY = CenterY( pItemPool->sGridNo );
  3482. IgniteExplosion( NOBODY, sX, sY, (INT16) (gpWorldLevelData[pItemPool->sGridNo].sHeight + pItemPool->bRenderZHeightAboveLevel), pItemPool->sGridNo, MINI_GRENADE, 0 );
  3483. RemoveItemFromPool( pItemPool->sGridNo, pItemPool->iItemIndex, pItemPool->ubLevel );
  3484. }
  3485. }
  3486. BOOLEAN ContinuePastBoobyTrap( SOLDIERTYPE * pSoldier, INT16 sGridNo, INT8 bLevel, INT32 iItemIndex, BOOLEAN fInStrategic, BOOLEAN *pfSaidQuote )
  3487. {
  3488. BOOLEAN fBoobyTrapKnowledge;
  3489. INT8 bTrapDifficulty, bTrapDetectLevel;
  3490. OBJECTTYPE * pObj;
  3491. pObj = &(gWorldItems[ iItemIndex ].o);
  3492. (*pfSaidQuote) = FALSE;
  3493. if (pObj->bTrap > 0)
  3494. {
  3495. if (pSoldier->bTeam == gbPlayerNum)
  3496. {
  3497. // does the player know about this item?
  3498. fBoobyTrapKnowledge = ((pObj->fFlags & OBJECT_KNOWN_TO_BE_TRAPPED) > 0);
  3499. // blue flag stuff?
  3500. if (!fBoobyTrapKnowledge)
  3501. {
  3502. bTrapDifficulty = pObj->bTrap;
  3503. bTrapDetectLevel = CalcTrapDetectLevel( pSoldier, FALSE );
  3504. if (bTrapDetectLevel >= bTrapDifficulty)
  3505. {
  3506. // spotted the trap!
  3507. pObj->fFlags |= OBJECT_KNOWN_TO_BE_TRAPPED;
  3508. fBoobyTrapKnowledge = TRUE;
  3509. // Make him warn us:
  3510. // Set things up..
  3511. gpBoobyTrapSoldier = pSoldier;
  3512. gpBoobyTrapItemPool = GetItemPoolForIndex( sGridNo, iItemIndex, pSoldier->bLevel );
  3513. gsBoobyTrapGridNo = sGridNo;
  3514. gbBoobyTrapLevel = pSoldier->bLevel;
  3515. gfDisarmingBuriedBomb = FALSE;
  3516. gbTrapDifficulty = bTrapDifficulty;
  3517. // And make the call for the dialogue
  3518. SetStopTimeQuoteCallback( BoobyTrapDialogueCallBack );
  3519. TacticalCharacterDialogue( pSoldier, QUOTE_BOOBYTRAP_ITEM );
  3520. (*pfSaidQuote) = TRUE;
  3521. return( FALSE );
  3522. }
  3523. }
  3524. gpBoobyTrapItemPool = GetItemPoolForIndex( sGridNo, iItemIndex, pSoldier->bLevel );
  3525. if (fBoobyTrapKnowledge)
  3526. {
  3527. // have the computer ask us if we want to proceed
  3528. gpBoobyTrapSoldier = pSoldier;
  3529. gsBoobyTrapGridNo = sGridNo;
  3530. gbBoobyTrapLevel = pSoldier->bLevel;
  3531. gfDisarmingBuriedBomb = FALSE;
  3532. gbTrapDifficulty = pObj->bTrap;
  3533. if( fInStrategic )
  3534. {
  3535. DoMessageBox( MSG_BOX_BASIC_STYLE, TacticalStr[ DISARM_BOOBYTRAP_PROMPT ], MAP_SCREEN, ( UINT8 )MSG_BOX_FLAG_YESNO, BoobyTrapInMapScreenMessageBoxCallBack, NULL );
  3536. }
  3537. else
  3538. {
  3539. DoMessageBox( MSG_BOX_BASIC_STYLE, TacticalStr[ DISARM_BOOBYTRAP_PROMPT ], GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_YESNO, BoobyTrapMessageBoxCallBack, NULL );
  3540. }
  3541. }
  3542. else
  3543. {
  3544. // oops!
  3545. SetOffBoobyTrap( gpBoobyTrapItemPool );
  3546. }
  3547. return( FALSE );
  3548. }
  3549. // else, enemies etc always know about boobytraps and are not affected by them
  3550. }
  3551. return( TRUE );
  3552. }
  3553. void BoobyTrapDialogueCallBack( void )
  3554. {
  3555. gfJustFoundBoobyTrap = TRUE;
  3556. // now prompt the user...
  3557. if( guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN )
  3558. {
  3559. DoScreenIndependantMessageBox( TacticalStr[ DISARM_BOOBYTRAP_PROMPT ], ( UINT8 )MSG_BOX_FLAG_YESNO, BoobyTrapInMapScreenMessageBoxCallBack );
  3560. }
  3561. else
  3562. {
  3563. DoScreenIndependantMessageBox( TacticalStr[ DISARM_BOOBYTRAP_PROMPT ], ( UINT8 )MSG_BOX_FLAG_YESNO, BoobyTrapMessageBoxCallBack );
  3564. }
  3565. }
  3566. void BoobyTrapMessageBoxCallBack( UINT8 ubExitValue )
  3567. {
  3568. if ( gfJustFoundBoobyTrap )
  3569. {
  3570. // NOW award for finding boobytrap
  3571. // WISDOM GAIN: Detected a booby-trap
  3572. StatChange( gpBoobyTrapSoldier, WISDOMAMT, (UINT16) (3 * gbTrapDifficulty), FALSE );
  3573. // EXPLOSIVES GAIN: Detected a booby-trap
  3574. StatChange( gpBoobyTrapSoldier, EXPLODEAMT, (UINT16) (3 * gbTrapDifficulty), FALSE );
  3575. gfJustFoundBoobyTrap = FALSE;
  3576. }
  3577. if (ubExitValue == MSG_BOX_RETURN_YES)
  3578. {
  3579. INT32 iCheckResult;
  3580. OBJECTTYPE Object;
  3581. iCheckResult = SkillCheck( gpBoobyTrapSoldier, DISARM_TRAP_CHECK, 0 );
  3582. if (iCheckResult >= 0)
  3583. {
  3584. // get the item
  3585. memcpy( &Object, &(gWorldItems[ gpBoobyTrapItemPool->iItemIndex ].o), sizeof( OBJECTTYPE ) );
  3586. // NB owner grossness... bombs 'owned' by the enemy are stored with side value 1 in
  3587. // the map. So if we want to detect a bomb placed by the player, owner is > 1, and
  3588. // owner - 2 gives the ID of the character who planted it
  3589. if ( Object.ubBombOwner > 1 && ( (INT32)Object.ubBombOwner - 2 >= gTacticalStatus.Team[ OUR_TEAM ].bFirstID && Object.ubBombOwner - 2 <= gTacticalStatus.Team[ OUR_TEAM ].bLastID ) )
  3590. {
  3591. // our own bomb! no exp
  3592. }
  3593. else
  3594. {
  3595. // disarmed a boobytrap!
  3596. StatChange( gpBoobyTrapSoldier, EXPLODEAMT, (UINT16) (6 * gbTrapDifficulty), FALSE );
  3597. // have merc say this is good
  3598. DoMercBattleSound( gpBoobyTrapSoldier, BATTLE_SOUND_COOL1 );
  3599. }
  3600. if (gfDisarmingBuriedBomb)
  3601. {
  3602. if (Object.usItem == SWITCH)
  3603. {
  3604. // give the player a remote trigger instead
  3605. CreateItem( REMOTEBOMBTRIGGER, (INT8) (1 + Random( 9 )), &Object );
  3606. }
  3607. else if (Object.usItem == ACTION_ITEM && Object.bActionValue != ACTION_ITEM_BLOW_UP )
  3608. {
  3609. // give the player a detonator instead
  3610. CreateItem( DETONATOR, (INT8) (1 + Random( 9 )), &Object );
  3611. }
  3612. else
  3613. {
  3614. // switch action item to the real item type
  3615. CreateItem( Object.usBombItem, Object.bBombStatus, &Object );
  3616. }
  3617. // remove any blue flag graphic
  3618. RemoveBlueFlag( gsBoobyTrapGridNo, gbBoobyTrapLevel );
  3619. }
  3620. else
  3621. {
  3622. Object.bTrap = 0;
  3623. Object.fFlags &= ~( OBJECT_KNOWN_TO_BE_TRAPPED );
  3624. }
  3625. // place it in the guy's inventory/cursor
  3626. if ( AutoPlaceObject( gpBoobyTrapSoldier, &Object, TRUE ) )
  3627. {
  3628. // remove it from the ground
  3629. RemoveItemFromPool( gsBoobyTrapGridNo, gpBoobyTrapItemPool->iItemIndex, gbBoobyTrapLevel );
  3630. }
  3631. else
  3632. {
  3633. // make sure the item in the world is untrapped
  3634. gWorldItems[ gpBoobyTrapItemPool->iItemIndex ].o.bTrap = 0;
  3635. gWorldItems[ gpBoobyTrapItemPool->iItemIndex ].o.fFlags &= ~( OBJECT_KNOWN_TO_BE_TRAPPED );
  3636. // ATE; If we failed to add to inventory, put failed one in our cursor...
  3637. gfDontChargeAPsToPickup = TRUE;
  3638. HandleAutoPlaceFail( gpBoobyTrapSoldier, gpBoobyTrapItemPool->iItemIndex, gsBoobyTrapGridNo );
  3639. RemoveItemFromPool( gsBoobyTrapGridNo, gpBoobyTrapItemPool->iItemIndex, gbBoobyTrapLevel );
  3640. }
  3641. }
  3642. else
  3643. {
  3644. // oops! trap goes off
  3645. StatChange( gpBoobyTrapSoldier, EXPLODEAMT, (INT8) (3 * gbTrapDifficulty ), FROM_FAILURE );
  3646. DoMercBattleSound( gpBoobyTrapSoldier, BATTLE_SOUND_CURSE1 );
  3647. if (gfDisarmingBuriedBomb)
  3648. {
  3649. SetOffBombsInGridNo( gpBoobyTrapSoldier->ubID, gsBoobyTrapGridNo, TRUE, gbBoobyTrapLevel );
  3650. }
  3651. else
  3652. {
  3653. SetOffBoobyTrap( gpBoobyTrapItemPool );
  3654. }
  3655. }
  3656. }
  3657. else
  3658. {
  3659. if (gfDisarmingBuriedBomb)
  3660. {
  3661. DoMessageBox( MSG_BOX_BASIC_STYLE, TacticalStr[ REMOVE_BLUE_FLAG_PROMPT ], GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_YESNO, RemoveBlueFlagDialogueCallBack, NULL );
  3662. }
  3663. // otherwise do nothing
  3664. }
  3665. }
  3666. void BoobyTrapInMapScreenMessageBoxCallBack( UINT8 ubExitValue )
  3667. {
  3668. if ( gfJustFoundBoobyTrap )
  3669. {
  3670. // NOW award for finding boobytrap
  3671. // WISDOM GAIN: Detected a booby-trap
  3672. StatChange( gpBoobyTrapSoldier, WISDOMAMT, (UINT16) (3 * gbTrapDifficulty), FALSE );
  3673. // EXPLOSIVES GAIN: Detected a booby-trap
  3674. StatChange( gpBoobyTrapSoldier, EXPLODEAMT, (UINT16) (3 * gbTrapDifficulty), FALSE );
  3675. gfJustFoundBoobyTrap = FALSE;
  3676. }
  3677. if (ubExitValue == MSG_BOX_RETURN_YES)
  3678. {
  3679. INT32 iCheckResult;
  3680. OBJECTTYPE Object;
  3681. iCheckResult = SkillCheck( gpBoobyTrapSoldier, DISARM_TRAP_CHECK, 0 );
  3682. if (iCheckResult >= 0)
  3683. {
  3684. // disarmed a boobytrap!
  3685. StatChange( gpBoobyTrapSoldier, EXPLODEAMT, (UINT16) (6 * gbTrapDifficulty), FALSE );
  3686. // have merc say this is good
  3687. DoMercBattleSound( gpBoobyTrapSoldier, BATTLE_SOUND_COOL1 );
  3688. // get the item
  3689. memcpy( &Object, gpItemPointer, sizeof( OBJECTTYPE ) );
  3690. if (gfDisarmingBuriedBomb)
  3691. {
  3692. if (Object.usItem == SWITCH)
  3693. {
  3694. // give the player a remote trigger instead
  3695. CreateItem( REMOTEBOMBTRIGGER, (INT8) (1 + Random( 9 )), &Object );
  3696. }
  3697. else if (Object.usItem == ACTION_ITEM && Object.bActionValue != ACTION_ITEM_BLOW_UP )
  3698. {
  3699. // give the player a detonator instead
  3700. CreateItem( DETONATOR, (INT8) (1 + Random( 9 )), &Object );
  3701. }
  3702. else
  3703. {
  3704. // switch action item to the real item type
  3705. CreateItem( Object.usBombItem, Object.bBombStatus, &Object );
  3706. }
  3707. }
  3708. else
  3709. {
  3710. Object.bTrap = 0;
  3711. Object.fFlags &= ~( OBJECT_KNOWN_TO_BE_TRAPPED );
  3712. }
  3713. MAPEndItemPointer( );
  3714. // place it in the guy's inventory/cursor
  3715. if ( !AutoPlaceObject( gpBoobyTrapSoldier, &Object, TRUE ) )
  3716. {
  3717. AutoPlaceObjectInInventoryStash( &Object );
  3718. }
  3719. HandleButtonStatesWhileMapInventoryActive( );
  3720. }
  3721. else
  3722. {
  3723. // oops! trap goes off
  3724. StatChange( gpBoobyTrapSoldier, EXPLODEAMT, (INT8) (3 * gbTrapDifficulty ), FROM_FAILURE );
  3725. DoMercBattleSound( gpBoobyTrapSoldier, BATTLE_SOUND_CURSE1 );
  3726. if (gfDisarmingBuriedBomb)
  3727. {
  3728. SetOffBombsInGridNo( gpBoobyTrapSoldier->ubID, gsBoobyTrapGridNo, TRUE, gbBoobyTrapLevel );
  3729. }
  3730. else
  3731. {
  3732. SetOffBoobyTrap( gpBoobyTrapItemPool );
  3733. }
  3734. }
  3735. }
  3736. else
  3737. {
  3738. if (gfDisarmingBuriedBomb)
  3739. {
  3740. DoMessageBox( MSG_BOX_BASIC_STYLE, TacticalStr[ REMOVE_BLUE_FLAG_PROMPT ], GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_YESNO, RemoveBlueFlagDialogueCallBack, NULL );
  3741. }
  3742. // otherwise do nothing
  3743. }
  3744. }
  3745. void SwitchMessageBoxCallBack( UINT8 ubExitValue )
  3746. {
  3747. if ( ubExitValue == MSG_BOX_RETURN_YES )
  3748. {
  3749. // Message that switch is activated...
  3750. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzLateLocalizedString[ 60 ] );
  3751. SetOffBombsByFrequency( gpTempSoldier->ubID, bTempFrequency );
  3752. }
  3753. }
  3754. BOOLEAN NearbyGroundSeemsWrong( SOLDIERTYPE * pSoldier, INT16 sGridNo, BOOLEAN fCheckAroundGridno, INT16 * psProblemGridNo )
  3755. {
  3756. INT16 sNextGridNo;
  3757. // BOOLEAN fWorthChecking = FALSE, fProblemExists = FALSE, fDetectedProblem = FALSE;
  3758. UINT8 ubDetectLevel, ubDirection;
  3759. MAP_ELEMENT * pMapElement;
  3760. UINT32 fCheckFlag;
  3761. UINT32 uiWorldBombIndex;
  3762. OBJECTTYPE * pObj;
  3763. BOOLEAN fMining, fFoundMetal = FALSE;
  3764. // ITEM_POOL * pItemPool;
  3765. UINT8 ubMovementCost;
  3766. ubDetectLevel = 0;
  3767. if ( FindObj( pSoldier, METALDETECTOR ) != NO_SLOT )
  3768. {
  3769. fMining = TRUE;
  3770. }
  3771. else
  3772. {
  3773. fMining = FALSE;
  3774. ubDetectLevel = CalcTrapDetectLevel( pSoldier, FALSE );
  3775. /*
  3776. if (pSoldier->bStealthMode)
  3777. {
  3778. ubDetectLevel++;
  3779. }
  3780. switch (pSoldier->usAnimState)
  3781. {
  3782. case CRAWLING:
  3783. ubDetectLevel += 2;
  3784. break;
  3785. case SWATTING:
  3786. ubDetectLevel++;
  3787. break;
  3788. default:
  3789. break;
  3790. }
  3791. */
  3792. }
  3793. if (pSoldier->bSide == 0)
  3794. {
  3795. fCheckFlag = MAPELEMENT_PLAYER_MINE_PRESENT;
  3796. }
  3797. else
  3798. {
  3799. fCheckFlag = MAPELEMENT_ENEMY_MINE_PRESENT;
  3800. }
  3801. // check every tile around gridno for the presence of "nasty stuff"
  3802. for (ubDirection = 0; ubDirection < 8; ubDirection++)
  3803. {
  3804. if ( fCheckAroundGridno )
  3805. {
  3806. // get the gridno of the next spot adjacent to lastGridno in that direction
  3807. sNextGridNo = NewGridNo( sGridNo, (INT16) DirectionInc( (UINT8) ubDirection ) );
  3808. // don't check directions that are impassable!
  3809. ubMovementCost = gubWorldMovementCosts[ sNextGridNo ][ ubDirection ][ pSoldier->bLevel ];
  3810. if ( IS_TRAVELCOST_DOOR( ubMovementCost ) )
  3811. {
  3812. ubMovementCost = DoorTravelCost( NULL, sNextGridNo, ubMovementCost, FALSE, NULL );
  3813. }
  3814. if ( ubMovementCost >= TRAVELCOST_BLOCKED)
  3815. {
  3816. continue;
  3817. }
  3818. }
  3819. else
  3820. {
  3821. // we should just be checking the gridno
  3822. sNextGridNo = sGridNo;
  3823. ubDirection = 8; // don't loop
  3824. }
  3825. // if this sNextGridNo isn't out of bounds... but it never can be
  3826. pMapElement = &(gpWorldLevelData[sNextGridNo]);
  3827. if (pMapElement->uiFlags & fCheckFlag)
  3828. {
  3829. // already know there's a mine there
  3830. continue;
  3831. }
  3832. // check for boobytraps
  3833. for (uiWorldBombIndex = 0; uiWorldBombIndex < guiNumWorldBombs; uiWorldBombIndex++)
  3834. {
  3835. if (gWorldBombs[uiWorldBombIndex].fExists && gWorldItems[ gWorldBombs[uiWorldBombIndex].iItemIndex ].sGridNo == sNextGridNo)
  3836. {
  3837. pObj = &( gWorldItems[ gWorldBombs[uiWorldBombIndex].iItemIndex ].o );
  3838. if ( pObj->bDetonatorType == BOMB_PRESSURE && !(pObj->fFlags & OBJECT_KNOWN_TO_BE_TRAPPED) && (!(pObj->fFlags & OBJECT_DISABLED_BOMB)) )
  3839. {
  3840. if ( fMining && pObj->bTrap <= 10 )
  3841. {
  3842. // add blue flag
  3843. AddBlueFlag( sNextGridNo, pSoldier->bLevel );
  3844. fFoundMetal = TRUE;
  3845. break;
  3846. }
  3847. else if (ubDetectLevel >= pObj->bTrap)
  3848. {
  3849. if (pSoldier->uiStatusFlags & SOLDIER_PC )
  3850. {
  3851. // detected exposives buried nearby...
  3852. StatChange( pSoldier, EXPLODEAMT, (UINT16) (pObj->bTrap), FALSE );
  3853. // set item as known
  3854. pObj->fFlags |= OBJECT_KNOWN_TO_BE_TRAPPED;
  3855. }
  3856. *psProblemGridNo = sNextGridNo;
  3857. return( TRUE );
  3858. }
  3859. }
  3860. }
  3861. }
  3862. /*
  3863. // also check for metal items if using a metal detector
  3864. if (fMining)
  3865. {
  3866. // add blue flags where we find metallic objects hidden
  3867. GetItemPool( sNextGridNo, &pItemPool, pSoldier->bLevel );
  3868. while( pItemPool )
  3869. {
  3870. if ( pItemPool->bVisible == BURIED || (pItemPool->bVisible != TRUE && gWorldItems[ pItemPool->iItemIndex ].o.bTrap > 0 ) )
  3871. {
  3872. pObj = &( gWorldItems[ pItemPool->iItemIndex ].o );
  3873. if ( pObj->usItem == ACTION_ITEM && pObj-> )
  3874. {
  3875. switch( pObj->bActionValue )
  3876. {
  3877. case ACTION_ITEM_BLOW_UP:
  3878. case ACTION_ITEM_LOCAL_ALARM:
  3879. case ACTION_ITEM_GLOBAL_ALARM:
  3880. // add blue flag
  3881. AddBlueFlag( sNextGridNo, pSoldier->bLevel );
  3882. fFoundMetal = TRUE;
  3883. break;
  3884. default:
  3885. break;
  3886. }
  3887. }
  3888. else if (Item[ pObj->usItem ].fFlags & ITEM_METAL)
  3889. {
  3890. // add blue flag
  3891. AddBlueFlag( sNextGridNo, pSoldier->bLevel );
  3892. fFoundMetal = TRUE;
  3893. break;
  3894. }
  3895. }
  3896. pItemPool = pItemPool->pNext;
  3897. }
  3898. }
  3899. */
  3900. }
  3901. *psProblemGridNo = NOWHERE;
  3902. if (fFoundMetal)
  3903. {
  3904. return( TRUE );
  3905. }
  3906. else
  3907. {
  3908. return( FALSE );
  3909. }
  3910. }
  3911. void MineSpottedDialogueCallBack( void )
  3912. {
  3913. ITEM_POOL * pItemPool;
  3914. // ATE: REALLY IMPORTANT - ALL CALLBACK ITEMS SHOULD UNLOCK
  3915. gTacticalStatus.fLockItemLocators = FALSE;
  3916. GetItemPool( gsBoobyTrapGridNo, &pItemPool, gbBoobyTrapLevel );
  3917. guiPendingOverrideEvent = LU_BEGINUILOCK;
  3918. // play a locator at the location of the mine
  3919. SetItemPoolLocatorWithCallback( pItemPool, MineSpottedLocatorCallback );
  3920. }
  3921. void MineSpottedLocatorCallback( void )
  3922. {
  3923. guiPendingOverrideEvent = LU_ENDUILOCK;
  3924. // now ask the player if he wants to place a blue flag.
  3925. DoMessageBox( MSG_BOX_BASIC_STYLE, TacticalStr[ PLACE_BLUE_FLAG_PROMPT ], GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_YESNO, MineSpottedMessageBoxCallBack, NULL );
  3926. }
  3927. void MineSpottedMessageBoxCallBack( UINT8 ubExitValue )
  3928. {
  3929. if (ubExitValue == MSG_BOX_RETURN_YES)
  3930. {
  3931. // place a blue flag where the mine was found
  3932. AddBlueFlag( gsBoobyTrapGridNo, gbBoobyTrapLevel );
  3933. }
  3934. }
  3935. void RemoveBlueFlagDialogueCallBack( UINT8 ubExitValue )
  3936. {
  3937. if (ubExitValue == MSG_BOX_RETURN_YES)
  3938. {
  3939. RemoveBlueFlag( gsBoobyTrapGridNo, gbBoobyTrapLevel );
  3940. }
  3941. }
  3942. void AddBlueFlag( INT16 sGridNo ,INT8 bLevel )
  3943. {
  3944. LEVELNODE *pNode;
  3945. ApplyMapChangesToMapTempFile( TRUE );
  3946. gpWorldLevelData[ sGridNo ].uiFlags |= MAPELEMENT_PLAYER_MINE_PRESENT;
  3947. pNode = AddStructToTail( sGridNo, BLUEFLAG_GRAPHIC );
  3948. if ( pNode )
  3949. {
  3950. pNode->uiFlags |= LEVELNODE_SHOW_THROUGH;
  3951. }
  3952. ApplyMapChangesToMapTempFile( FALSE );
  3953. RecompileLocalMovementCostsFromRadius( sGridNo, bLevel );
  3954. SetRenderFlags(RENDER_FLAG_FULL);
  3955. }
  3956. void RemoveBlueFlag( INT16 sGridNo, INT8 bLevel )
  3957. {
  3958. ApplyMapChangesToMapTempFile( TRUE );
  3959. gpWorldLevelData[sGridNo].uiFlags &= ~(MAPELEMENT_PLAYER_MINE_PRESENT);
  3960. if ( bLevel == 0 )
  3961. {
  3962. RemoveStruct( sGridNo, BLUEFLAG_GRAPHIC );
  3963. }
  3964. else
  3965. {
  3966. RemoveOnRoof( sGridNo, BLUEFLAG_GRAPHIC );
  3967. }
  3968. ApplyMapChangesToMapTempFile( FALSE );
  3969. RecompileLocalMovementCostsFromRadius( sGridNo, bLevel );
  3970. SetRenderFlags(RENDER_FLAG_FULL);
  3971. }
  3972. void MakeNPCGrumpyForMinorOffense( SOLDIERTYPE * pSoldier, SOLDIERTYPE *pOffendingSoldier )
  3973. {
  3974. CancelAIAction( pSoldier, TRUE );
  3975. switch( pSoldier->ubProfile )
  3976. {
  3977. case FREDO:
  3978. case FRANZ:
  3979. case HERVE:
  3980. case PETER:
  3981. case ALBERTO:
  3982. case CARLO:
  3983. case MANNY:
  3984. case GABBY:
  3985. case ARNIE:
  3986. case HOWARD:
  3987. case SAM:
  3988. case FATHER:
  3989. case TINA:
  3990. case ARMAND:
  3991. case WALTER:
  3992. gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags3 |= PROFILE_MISC_FLAG3_NPC_PISSED_OFF;
  3993. TriggerNPCWithIHateYouQuote( pSoldier->ubProfile );
  3994. break;
  3995. default:
  3996. // trigger NPCs with quote if available
  3997. AddToShouldBecomeHostileOrSayQuoteList( pSoldier->ubID );
  3998. break;
  3999. }
  4000. if ( pOffendingSoldier )
  4001. {
  4002. pSoldier->bNextAction = AI_ACTION_CHANGE_FACING;
  4003. pSoldier->usNextActionData = atan8( pSoldier->sX, pSoldier->sY, pOffendingSoldier->sX, pOffendingSoldier->sY );
  4004. }
  4005. }
  4006. void TestPotentialOwner( SOLDIERTYPE * pSoldier )
  4007. {
  4008. if ( pSoldier->bActive && pSoldier->bInSector && pSoldier->bLife >= OKLIFE )
  4009. {
  4010. if ( SoldierToSoldierLineOfSightTest( pSoldier, gpTempSoldier, (UINT8) DistanceVisible( pSoldier, DIRECTION_IRRELEVANT, 0, gpTempSoldier->sGridNo, gpTempSoldier->bLevel ), TRUE ) )
  4011. {
  4012. MakeNPCGrumpyForMinorOffense( pSoldier, gpTempSoldier );
  4013. }
  4014. }
  4015. }
  4016. void CheckForPickedOwnership( void )
  4017. {
  4018. ITEM_POOL * pItemPool;
  4019. UINT8 ubProfile;
  4020. UINT8 ubCivGroup;
  4021. SOLDIERTYPE * pSoldier;
  4022. UINT8 ubLoop;
  4023. // LOOP THROUGH LIST TO FIND NODE WE WANT
  4024. GetItemPool( gsTempGridno, &pItemPool, gpTempSoldier->bLevel );
  4025. while( pItemPool )
  4026. {
  4027. if ( gWorldItems[ pItemPool->iItemIndex ].o.usItem == OWNERSHIP )
  4028. {
  4029. if ( gWorldItems[ pItemPool->iItemIndex ].o.ubOwnerProfile != NO_PROFILE )
  4030. {
  4031. ubProfile = gWorldItems[ pItemPool->iItemIndex ].o.ubOwnerProfile;
  4032. pSoldier = FindSoldierByProfileID( ubProfile, FALSE );
  4033. if ( pSoldier )
  4034. {
  4035. TestPotentialOwner( pSoldier );
  4036. }
  4037. }
  4038. if ( gWorldItems[ pItemPool->iItemIndex ].o.ubOwnerCivGroup != NON_CIV_GROUP )
  4039. {
  4040. ubCivGroup = gWorldItems[ pItemPool->iItemIndex ].o.ubOwnerCivGroup;
  4041. if ( ubCivGroup == HICKS_CIV_GROUP && CheckFact( FACT_HICKS_MARRIED_PLAYER_MERC, 0 ) )
  4042. {
  4043. // skip because hicks appeased
  4044. pItemPool = pItemPool->pNext;
  4045. continue;
  4046. }
  4047. for ( ubLoop = gTacticalStatus.Team[ CIV_TEAM ].bFirstID; ubLoop <= gTacticalStatus.Team[ CIV_TEAM ].bLastID; ubLoop++ )
  4048. {
  4049. pSoldier = MercPtrs[ ubLoop ];
  4050. if ( pSoldier && pSoldier->ubCivilianGroup == ubCivGroup )
  4051. {
  4052. TestPotentialOwner( pSoldier );
  4053. }
  4054. }
  4055. }
  4056. }
  4057. pItemPool = pItemPool->pNext;
  4058. }
  4059. }
  4060. void LoopLevelNodeForItemGlowFlag( LEVELNODE *pNode, INT16 sGridNo, UINT8 ubLevel, BOOLEAN fOn )
  4061. {
  4062. while ( pNode != NULL )
  4063. {
  4064. if ( pNode->uiFlags & LEVELNODE_ITEM )
  4065. {
  4066. if ( fOn )
  4067. {
  4068. pNode->uiFlags |= LEVELNODE_DYNAMIC;
  4069. }
  4070. else
  4071. {
  4072. pNode->uiFlags &= (~LEVELNODE_DYNAMIC);
  4073. }
  4074. }
  4075. pNode = pNode->pNext;
  4076. }
  4077. }
  4078. void HandleItemGlowFlag( INT16 sGridNo, UINT8 ubLevel, BOOLEAN fOn )
  4079. {
  4080. LEVELNODE *pNode;
  4081. if ( ubLevel == 0 )
  4082. {
  4083. pNode = gpWorldLevelData[ sGridNo ].pStructHead;
  4084. LoopLevelNodeForItemGlowFlag( pNode, sGridNo, ubLevel, fOn );
  4085. }
  4086. else
  4087. {
  4088. pNode = gpWorldLevelData[ sGridNo ].pOnRoofHead;
  4089. LoopLevelNodeForItemGlowFlag( pNode, sGridNo, ubLevel, fOn );
  4090. }
  4091. }
  4092. void ToggleItemGlow( BOOLEAN fOn )
  4093. {
  4094. UINT32 cnt;
  4095. for ( cnt = 0; cnt < WORLD_MAX; cnt++ )
  4096. {
  4097. HandleItemGlowFlag( ( INT16 )cnt, 0, fOn );
  4098. HandleItemGlowFlag( ( INT16 )cnt, 1, fOn );
  4099. }
  4100. if ( !fOn )
  4101. {
  4102. gGameSettings.fOptions[ TOPTION_GLOW_ITEMS ] = FALSE;
  4103. }
  4104. else
  4105. {
  4106. gGameSettings.fOptions[ TOPTION_GLOW_ITEMS ] = TRUE;
  4107. }
  4108. SetRenderFlags(RENDER_FLAG_FULL);
  4109. }
  4110. BOOLEAN ContinuePastBoobyTrapInMapScreen( OBJECTTYPE *pObject, SOLDIERTYPE *pSoldier )
  4111. {
  4112. BOOLEAN fBoobyTrapKnowledge;
  4113. INT8 bTrapDifficulty, bTrapDetectLevel;
  4114. if (pObject->bTrap > 0)
  4115. {
  4116. if (pSoldier->bTeam == gbPlayerNum)
  4117. {
  4118. // does the player know about this item?
  4119. fBoobyTrapKnowledge = ((pObject->fFlags & OBJECT_KNOWN_TO_BE_TRAPPED) > 0);
  4120. // blue flag stuff?
  4121. if (!fBoobyTrapKnowledge)
  4122. {
  4123. bTrapDifficulty = pObject->bTrap;
  4124. bTrapDetectLevel = CalcTrapDetectLevel( pSoldier, FALSE );
  4125. if (bTrapDetectLevel >= bTrapDifficulty)
  4126. {
  4127. // spotted the trap!
  4128. pObject->fFlags |= OBJECT_KNOWN_TO_BE_TRAPPED;
  4129. fBoobyTrapKnowledge = TRUE;
  4130. // Make him warn us:
  4131. gpBoobyTrapSoldier = pSoldier;
  4132. // And make the call for the dialogue
  4133. SetStopTimeQuoteCallback( BoobyTrapDialogueCallBack );
  4134. TacticalCharacterDialogue( pSoldier, QUOTE_BOOBYTRAP_ITEM );
  4135. return( FALSE );
  4136. }
  4137. }
  4138. if (fBoobyTrapKnowledge)
  4139. {
  4140. // have the computer ask us if we want to proceed
  4141. gpBoobyTrapSoldier = pSoldier;
  4142. gbTrapDifficulty = pObject->bTrap;
  4143. DoMessageBox( MSG_BOX_BASIC_STYLE, TacticalStr[ DISARM_BOOBYTRAP_PROMPT ], MAP_SCREEN, ( UINT8 )MSG_BOX_FLAG_YESNO, BoobyTrapInMapScreenMessageBoxCallBack, NULL );
  4144. }
  4145. else
  4146. {
  4147. // oops!
  4148. SetOffBoobyTrapInMapScreen( pSoldier, pObject );
  4149. }
  4150. return( FALSE );
  4151. }
  4152. // else, enemies etc always know about boobytraps and are not affected by them
  4153. }
  4154. return( TRUE );
  4155. }
  4156. // Well, clears all item pools
  4157. void ClearAllItemPools( )
  4158. {
  4159. UINT32 cnt;
  4160. for ( cnt = 0; cnt < WORLD_MAX; cnt++ )
  4161. {
  4162. RemoveItemPool( (INT16)cnt, 0 );
  4163. RemoveItemPool( (INT16)cnt, 1 );
  4164. }
  4165. }
  4166. // Refresh item pools
  4167. void RefreshItemPools( WORLDITEM * pItemList, INT32 iNumberOfItems )
  4168. {
  4169. ClearAllItemPools( );
  4170. RefreshWorldItemsIntoItemPools( pItemList, iNumberOfItems );
  4171. }
  4172. INT16 FindNearestAvailableGridNoForItem( INT16 sSweetGridNo, INT8 ubRadius )
  4173. {
  4174. INT16 sTop, sBottom;
  4175. INT16 sLeft, sRight;
  4176. INT16 cnt1, cnt2, cnt3;
  4177. INT16 sGridNo;
  4178. INT32 uiRange, uiLowestRange = 999999;
  4179. INT16 sLowestGridNo=0;
  4180. INT32 leftmost;
  4181. BOOLEAN fFound = FALSE;
  4182. SOLDIERTYPE soldier;
  4183. UINT8 ubSaveNPCAPBudget;
  4184. UINT8 ubSaveNPCDistLimit;
  4185. BOOLEAN fSetDirection = FALSE;
  4186. cnt3 = 0;
  4187. //Save AI pathing vars. changing the distlimit restricts how
  4188. //far away the pathing will consider.
  4189. ubSaveNPCAPBudget = gubNPCAPBudget;
  4190. ubSaveNPCDistLimit = gubNPCDistLimit;
  4191. gubNPCAPBudget = 0;
  4192. gubNPCDistLimit = ubRadius;
  4193. //create dummy soldier, and use the pathing to determine which nearby slots are
  4194. //reachable.
  4195. memset( &soldier, 0, sizeof( SOLDIERTYPE ) );
  4196. soldier.bTeam = 1;
  4197. soldier.sGridNo = sSweetGridNo;
  4198. sTop = ubRadius;
  4199. sBottom = -ubRadius;
  4200. sLeft = - ubRadius;
  4201. sRight = ubRadius;
  4202. //clear the mapelements of potential residue MAPELEMENT_REACHABLE flags
  4203. //in the square region.
  4204. for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
  4205. {
  4206. for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ )
  4207. {
  4208. sGridNo = sSweetGridNo + (WORLD_COLS * cnt1) + cnt2;
  4209. if ( sGridNo >=0 && sGridNo < WORLD_MAX )
  4210. {
  4211. gpWorldLevelData[ sGridNo ].uiFlags &= (~MAPELEMENT_REACHABLE);
  4212. }
  4213. }
  4214. }
  4215. //Now, find out which of these gridnos are reachable
  4216. //(use the fake soldier and the pathing settings)
  4217. FindBestPath( &soldier, NOWHERE, 0, WALKING, COPYREACHABLE, 0 );
  4218. uiLowestRange = 999999;
  4219. for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
  4220. {
  4221. leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS;
  4222. for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ )
  4223. {
  4224. sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2;
  4225. if ( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) &&
  4226. gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE )
  4227. {
  4228. // Go on sweet stop
  4229. if ( NewOKDestination( &soldier, sGridNo, TRUE, soldier.bLevel ) )
  4230. {
  4231. uiRange = GetRangeInCellCoordsFromGridNoDiff( sSweetGridNo, sGridNo );
  4232. if ( uiRange < uiLowestRange )
  4233. {
  4234. sLowestGridNo = sGridNo;
  4235. uiLowestRange = uiRange;
  4236. fFound = TRUE;
  4237. }
  4238. }
  4239. }
  4240. }
  4241. }
  4242. gubNPCAPBudget = ubSaveNPCAPBudget;
  4243. gubNPCDistLimit = ubSaveNPCDistLimit;
  4244. if ( fFound )
  4245. {
  4246. return sLowestGridNo;
  4247. }
  4248. return NOWHERE;
  4249. }
  4250. BOOLEAN CanPlayerUseRocketRifle( SOLDIERTYPE *pSoldier, BOOLEAN fDisplay )
  4251. {
  4252. if ( pSoldier->inv[ pSoldier->ubAttackingHand ].usItem == ROCKET_RIFLE || pSoldier->inv[ pSoldier->ubAttackingHand ].usItem == AUTO_ROCKET_RIFLE )
  4253. {
  4254. // check imprint ID
  4255. // NB not-imprinted value is NO_PROFILE
  4256. // imprinted value is profile for mercs & NPCs and NO_PROFILE + 1 for generic dudes
  4257. if (pSoldier->ubProfile != NO_PROFILE)
  4258. {
  4259. if ( pSoldier->inv[ pSoldier->ubAttackingHand ].ubImprintID != pSoldier->ubProfile )
  4260. {
  4261. // NOT a virgin gun...
  4262. if ( pSoldier->inv[ pSoldier->ubAttackingHand ].ubImprintID != NO_PROFILE )
  4263. {
  4264. // access denied!
  4265. if (pSoldier->bTeam == gbPlayerNum)
  4266. {
  4267. PlayJA2Sample( RG_ID_INVALID, RATE_11025, HIGHVOLUME, 1, MIDDLE );
  4268. if ( fDisplay )
  4269. {
  4270. ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, L"\"%s\"", TacticalStr[ GUN_NOGOOD_FINGERPRINT ] );
  4271. }
  4272. }
  4273. return( FALSE );
  4274. }
  4275. }
  4276. }
  4277. }
  4278. return( TRUE );
  4279. }