1 // Crimson Fields -- a game of tactical warfare
2 // Copyright (C) 2000-2007 Jens Granseuer
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 //
18 
19 ////////////////////////////////////////////////////////////////////////
20 // history.cpp
21 ////////////////////////////////////////////////////////////////////////
22 
23 #include "history.h"
24 #include "game.h"
25 #include "options.h"
26 #include "msgs.h"
27 
28 extern Options CFOptions;
29 
30 ////////////////////////////////////////////////////////////////////////
31 // NAME       : HistEvent::Load
32 // DESCRIPTION: Load an event from a file.
33 // PARAMETERS : file - file descriptor
34 // RETURNS    : -1 on error, 0 otherwise
35 ////////////////////////////////////////////////////////////////////////
36 
Load(MemBuffer & file)37 int HistEvent::Load( MemBuffer &file ) {
38   type = file.Read8();
39   for ( int i = 0; i < 4; ++i ) data[i] = file.Read16();
40   return 0;
41 }
42 
43 ////////////////////////////////////////////////////////////////////////
44 // NAME       : HistEvent::Save
45 // DESCRIPTION: Save an event to a file.
46 // PARAMETERS : file - file descriptor
47 // RETURNS    : 0 on success, non-0 otherwise
48 ////////////////////////////////////////////////////////////////////////
49 
Save(MemBuffer & file) const50 int HistEvent::Save( MemBuffer &file ) const {
51   file.Write8( type );
52   for ( int i = 0; i < 4; ++i ) file.Write16( data[i] );
53   return 0;
54 }
55 
56 
57 ////////////////////////////////////////////////////////////////////////
58 // NAME       : History::Load
59 // DESCRIPTION: Load a History from a file.
60 // PARAMETERS : file    - open file descriptor
61 //              mission - current mission
62 // RETURNS    : -1 on error, number of events loaded otherwise
63 ////////////////////////////////////////////////////////////////////////
64 
Load(MemBuffer & file,Mission & mission)65 short History::Load( MemBuffer &file, Mission &mission ) {
66   unsigned short num_events = file.Read16();
67 
68   if ( num_events > 0 ) {
69     unsigned short num_units, i;
70     num_units = file.Read16();
71 
72     for ( i = 0; i < num_events; ++i ) {
73       HistEvent *he = new HistEvent();
74       he->Load( file );
75       events.AddTail( he );
76     }
77 
78     for ( i = 0; i < num_units; ++i ) {
79       Unit *u = mission.LoadUnit( file, true );
80       if ( u ) units.AddTail( u );
81     }
82   }
83   return num_events;
84 }
85 
86 ////////////////////////////////////////////////////////////////////////
87 // NAME       : History::Save
88 // DESCRIPTION: Save a history to file.
89 // PARAMETERS : file    - open file descriptor
90 //              network - do some special processing if saving for
91 //                        sending over the wire in a network game
92 // RETURNS    : 0 on success, -1 on error
93 ////////////////////////////////////////////////////////////////////////
94 
Save(MemBuffer & file,bool network) const95 int History::Save( MemBuffer &file, bool network /* = false */ ) const {
96   unsigned short num;
97 
98   if ( network ) {
99     num = 0;
100     for ( HistEvent *he = static_cast<HistEvent *>( events.Head() );
101           he; he = static_cast<HistEvent *>( he->Next() ) ) {
102       // don't resend any events
103       if ( !he->processed ) {
104         if ( he->type == HIST_MOVE ||
105              (he->type == HIST_UNIT && he->data[1] != HIST_UEVENT_DESTROY) ||
106              he->type == HIST_ATTACK || he->type == HIST_COMBAT ||
107              he->type == HIST_TRANSPORT_CRYSTALS || he->type == HIST_TRANSPORT_UNIT )
108           ++num;
109         else
110           he->processed = true;
111       }
112 
113     }
114   } else num = events.CountNodes();
115 
116   file.Write16( num );
117   if ( num == 0 ) return 0;
118 
119   file.Write16( units.CountNodes() );
120 
121   // save events
122   for ( HistEvent *he = static_cast<HistEvent *>( events.Head() );
123         he; he = static_cast<HistEvent *>( he->Next() ) )
124     if ( !network || !he->processed )
125       he->Save( file );
126 
127   // save units
128   for ( Unit *u = static_cast<Unit *>( units.Head() );
129         u; u = static_cast<Unit *>( u->Next() ) )
130     u->Save( file );
131   return 0;
132 }
133 
134 ////////////////////////////////////////////////////////////////////////
135 // NAME       : History::StartRecording
136 // DESCRIPTION: Create a new (empty) History from the current game.
137 // PARAMETERS : list - list of units
138 // RETURNS    : -
139 ////////////////////////////////////////////////////////////////////////
140 
StartRecording(List & list)141 void History::StartRecording( List &list ) {
142   // create the unit copies
143   for ( Unit *u = static_cast<Unit *>( list.Head() );
144         u; u = static_cast<Unit *>( u->Next() ) ) {
145     Unit *dummy = new Unit( *u );
146     dummy->SetFlags( U_DUMMY );
147     units.AddTail( dummy );
148   }
149 }
150 
151 ////////////////////////////////////////////////////////////////////////
152 // NAME       : History::Replay
153 // DESCRIPTION: Play all events that have been stored during the last
154 //              turn.
155 // PARAMETERS : mapwin - pointer to MapWindow
156 // RETURNS    : -
157 ////////////////////////////////////////////////////////////////////////
158 
Replay(MapWindow * mapwin)159 void History::Replay( MapWindow *mapwin ) {
160   if ( events.IsEmpty() ) return;
161 
162   View *view = mapwin->GetView();
163   MapView *mv = mapwin->GetMapView();
164   Map *map = mv->GetMap();
165   ProgressWindow *progwin = NULL;
166   HistEvent *he = static_cast<HistEvent *>( events.Head() );
167 
168   bool abort = false;
169   bool replay = CFOptions.GetTurnReplay();
170   bool quick = CFOptions.GetQuickReplay();
171 
172   if ( replay ) {
173     List units_bak;
174     BeginReplay( units_bak, map );
175 
176     if ( quick ) delay = 0;
177     else {
178       unsigned short count = events.CountNodes();
179 
180       // undo the map tile changes which have been recorded this turn
181       while ( he && (he->type == HIST_TILE_INTERNAL) ) {
182         map->SetHexType( he->data[1], he->data[2], he->data[0] );
183         he = static_cast<HistEvent *>( he->Next() );
184         --count;
185       }
186 
187       delay = DEFAULT_DELAY;
188       mv->Enable();
189       mapwin->Draw();
190       mapwin->Show();
191 
192       progwin = new ProgressWindow( mapwin->Width() / 4,
193               mapwin->Height() - view->SmallFont()->Height() - 30,
194               mapwin->Width() / 2, view->SmallFont()->Height() + 20,
195               0, count, NULL, WIN_PROG_ABORT|WIN_PROG_DEFAULT, view );
196     }
197 
198     while ( he && !abort ) {
199 
200       if ( he->type == HIST_MOVE )
201         ReplayMoveEvent( *he, mapwin );
202       else if ( he->type == HIST_COMBAT )
203         ReplayCombatEvent( *he, mapwin );
204       else if ( he->type == HIST_MSG )
205         ReplayMessageEvent( *he, view );
206       else if ( he->type == HIST_TILE )
207         ReplayTileEvent( *he, mapwin );
208       else if ( he->type == HIST_UNIT )
209         ReplayUnitEvent( *he, mapwin );
210 
211       else if ( !quick ) {
212         // these events are skipped completely for quick replays
213         if ( he->type == HIST_ATTACK )
214           ReplayAttackEvent( *he, mapwin );
215       }
216 
217       if ( !quick ) {
218         progwin->Advance( 1 );
219         abort = progwin->Cancelled();
220       }
221 
222       he = static_cast<HistEvent *>( he->Next() );
223     }
224 
225     EndReplay( units_bak, map );
226     if ( progwin ) view->CloseWindow( progwin );
227   }
228 
229 
230   if ( replay && !abort ) SDL_Delay( delay * 2 );
231   else if ( !quick ) {
232     // if replay has been stopped or disabled we still need to revert
233     // all changes to the map and display the cached messages
234     while ( he ) {
235       if ( he->type == HIST_TILE ) map->SetHexType( he->data[1], he->data[2], he->data[0] );
236       else if ( he->type == HIST_MSG ) ReplayMessageEvent( *he, view );
237       he = static_cast<HistEvent *>( he->Next() );
238     }
239   }
240 }
241 
242 ////////////////////////////////////////////////////////////////////////
243 // NAME       : History::BeginReplay
244 // DESCRIPTION: Prepare for viewing the last turn replay. We must store
245 //              the current list of units in a safe place and replace
246 //              them by their "historical" versions.
247 // PARAMETERS : backup - list to store current unit list in
248 //              map    - map
249 // RETURNS    : -
250 ////////////////////////////////////////////////////////////////////////
251 
BeginReplay(List & backup,Map * map)252 void History::BeginReplay( List &backup, Map *map ) {
253   List &gam_units = Gam->GetMission()->GetUnits();
254   Unit *u;
255 
256   while ( !gam_units.IsEmpty() ) {
257     u = static_cast<Unit *>( gam_units.RemHead() );
258     backup.AddTail( u );
259     if ( !u->IsSheltered() ) map->SetUnit( NULL, u->Position() );
260   }
261 
262   Unit *next = NULL;
263   for ( u = static_cast<Unit *>(units.Head()); u; u = next ) {
264     next = static_cast<Unit *>( u->Next() );
265 
266     // we use the BUSY flag internally to signal units which do
267     // not yet exist at the time replay is started
268     if ( !u->IsBusy() ) {
269       u->Remove();
270       gam_units.AddTail( u );
271       if ( !u->IsSheltered() ) map->SetUnit( u, u->Position() );
272     }
273   }
274 
275   lastunit = NULL;
276   lastpos = Point(-1, -1);
277 }
278 
279 ////////////////////////////////////////////////////////////////////////
280 // NAME       : History::EndReplay
281 // DESCRIPTION: End replay mode and initiate the next turn. Remove the
282 //              historical units and put the real ones back in place.
283 // PARAMETERS : backup - list containing the real units
284 //              map    - map
285 // RETURNS    : -
286 ////////////////////////////////////////////////////////////////////////
287 
EndReplay(List & backup,Map * map)288 void History::EndReplay( List &backup, Map *map ) {
289   List &gam_units = Gam->GetMission()->GetUnits();
290   Unit *u;
291 
292   while ( !gam_units.IsEmpty() ) {
293     u = static_cast<Unit *>( gam_units.RemHead() );
294     if ( !u->IsSheltered() ) map->SetUnit( NULL, u->Position() );
295     delete u;
296   }
297 
298   while ( !backup.IsEmpty() ) {
299     u = static_cast<Unit *>( backup.RemHead() );
300     gam_units.AddTail( u );
301     if ( !u->IsSheltered() ) map->SetUnit( u, u->Position() );
302   }
303 }
304 
305 ////////////////////////////////////////////////////////////////////////
306 // NAME       : History::RecordMoveEvent
307 // DESCRIPTION: Record the movement of a unit from one hex to another,
308 //              neighboring hex.
309 // PARAMETERS : u   - unit to be moved
310 //              dir - direction to move the unit in
311 // RETURNS    : 0 on success, -1 on error
312 ////////////////////////////////////////////////////////////////////////
313 
RecordMoveEvent(const Unit & u,Direction dir)314 int History::RecordMoveEvent( const Unit &u, Direction dir ) {
315   HistEvent *he = new HistEvent;
316 
317   he->type = HIST_MOVE;
318   he->data[0] = u.ID();
319   he->data[1] = dir;
320   he->data[2] = he->data[3] = -1;
321   events.AddTail( he );
322   return 0;
323 }
324 
325 ////////////////////////////////////////////////////////////////////////
326 // NAME       : History::RecordAttackEvent
327 // DESCRIPTION: Record an attack being inititated.
328 // PARAMETERS : u      - unit to inititate the attack
329 //              target - unit being attacked
330 // RETURNS    : 0 on success, -1 on error
331 ////////////////////////////////////////////////////////////////////////
332 
RecordAttackEvent(const Unit & u,const Unit & target)333 int History::RecordAttackEvent( const Unit &u, const Unit &target ) {
334   HistEvent *he = new HistEvent;
335 
336   he->type = HIST_ATTACK;
337   he->data[0] = u.ID();
338   he->data[1] = target.Position().x;
339   he->data[2] = target.Position().y;
340   he->data[3] = -1;
341 
342   events.AddTail( he );
343   return 0;
344 }
345 
346 ////////////////////////////////////////////////////////////////////////
347 // NAME       : History::RecordCombatEvent
348 // DESCRIPTION: Record the results of a combat.
349 // PARAMETERS : combat - combat data
350 //              loss1  - attackers' casualties
351 //              loss2  - defenders' casualties
352 // RETURNS    : 0 on success, -1 on error
353 ////////////////////////////////////////////////////////////////////////
354 
RecordCombatEvent(const Combat & combat,unsigned char loss1,unsigned char loss2)355 int History::RecordCombatEvent( const Combat &combat, unsigned char loss1,
356                                 unsigned char loss2 ) {
357   HistEvent *he = new HistEvent;
358 
359   he->type = HIST_COMBAT;
360   he->data[0] = combat.GetAttacker()->ID();
361   he->data[1] = combat.GetDefender()->ID();
362   he->data[2] = loss1;
363   he->data[3] = loss2;
364 
365   events.AddTail( he );
366   return 0;
367 }
368 
369 ////////////////////////////////////////////////////////////////////////
370 // NAME       : History::RecordTileEvent
371 // DESCRIPTION: Record a modification of the map. This event is special
372 //              in so far as two separate events are created for each
373 //              event. One allows for the initial map to be created from
374 //              the map at the end of the turn, the other represents the
375 //              actual map change.
376 // PARAMETERS : tile - tile type being set
377 //              old  - tile type being replaced
378 //              dx   - x hex being modified
379 //              dy   - y hex being modified
380 // RETURNS    : 0 on success, -1 on error
381 ////////////////////////////////////////////////////////////////////////
382 
RecordTileEvent(unsigned short tile,unsigned short old,short dx,short dy)383 int History::RecordTileEvent( unsigned short tile, unsigned short old,
384                               short dx, short dy ) {
385   HistEvent *he = new HistEvent;
386 
387   // first record the actual change
388   he->type = HIST_TILE;
389   he->data[0] = tile;
390   he->data[1] = dx;
391   he->data[2] = dy;
392   he->data[3] = -1;
393   events.AddTail( he );
394 
395   // now create the map state initializer event and put it at the
396   // head of the list
397   he = new HistEvent;
398   he->type = HIST_TILE_INTERNAL;
399   he->data[0] = old;
400   he->data[1] = dx;
401   he->data[2] = dy;
402   he->data[3] = -1;
403   events.AddHead( he );
404   return 0;
405 }
406 
407 ////////////////////////////////////////////////////////////////////////
408 // NAME       : History::RecordMsgEvent
409 // DESCRIPTION: Record a message for the next player.
410 // PARAMETERS : title - title to use for the message window. If -1,
411 //                      used title will be "Message".
412 //              msg   - index of the message
413 //              pos   - location index of a hex on the map that should
414 //                      be shown when displaying the message
415 // RETURNS    : 0 on success, -1 on error
416 ////////////////////////////////////////////////////////////////////////
417 
RecordMsgEvent(short title,unsigned short msg,short pos)418 int History::RecordMsgEvent( short title, unsigned short msg, short pos ) {
419   HistEvent *he = new HistEvent;
420 
421   he->type = HIST_MSG;
422   he->data[0] = title;
423   he->data[1] = msg;
424   he->data[2] = pos;
425   he->data[3] = -1;
426 
427   events.AddTail( he );
428   return 0;
429 }
430 
431 ////////////////////////////////////////////////////////////////////////
432 // NAME       : History::RecordUnitEvent
433 // DESCRIPTION: Record the creation or destruction of a unit.
434 // PARAMETERS : unit - created or destroyed unit
435 //              type - type of event (created, destroyed, repaired)
436 // RETURNS    : 0 on success, -1 on error
437 ////////////////////////////////////////////////////////////////////////
438 
RecordUnitEvent(const Unit & unit,HistUnitEventType type)439 int History::RecordUnitEvent( const Unit &unit, HistUnitEventType type ) {
440   HistEvent *he = new HistEvent;
441 
442   he->type = HIST_UNIT;
443   he->data[0] = unit.ID();
444   he->data[1] = type;
445   he->data[2] = he->data[3] = -1;
446 
447   events.AddTail( he );
448 
449   if ( type == HIST_UEVENT_CREATE ) {
450     Unit *clone = new Unit( unit );
451     clone->SetFlags( U_BUSY|U_DUMMY );
452     units.AddTail( clone );
453   }
454 
455   return 0;
456 }
457 
458 ////////////////////////////////////////////////////////////////////////
459 // NAME       : History::RecordTransportEvent
460 // DESCRIPTION: Record the (un)loading of crystals.
461 // PARAMETERS : source   - outer container to give/receive crystals
462 //              dest     - inner container to give/receive crystals
463 //              crystals - number of crystals to move from source to
464 //                         destination (may be negative)
465 // RETURNS    : 0 on success, -1 on error
466 ////////////////////////////////////////////////////////////////////////
467 
RecordTransportEvent(const UnitContainer & source,const Transport & dest,short crystals)468 int History::RecordTransportEvent( const UnitContainer &source,
469                             const Transport &dest, short crystals ) {
470   HistEvent *he = new HistEvent;
471 
472   he->type = HIST_TRANSPORT_CRYSTALS;
473   he->data[0] = dest.ID();
474   he->data[1] = crystals;
475   he->data[2] = he->data[3] = -1;
476   events.AddTail( he );
477   return 0;
478 }
479 
480 ////////////////////////////////////////////////////////////////////////
481 // NAME       : History::RecordTransportEvent
482 // DESCRIPTION: Record the movement of a unit from one container into
483 //              another (without leaving the current hex).
484 // PARAMETERS : source - outer container to remove unit from
485 //              dest   - inner container to add unit to
486 //              u      - unit to move from source to dest
487 // RETURNS    : 0 on success, -1 on error
488 ////////////////////////////////////////////////////////////////////////
489 
RecordTransportEvent(const UnitContainer & source,const Transport & dest,const Unit & u)490 int History::RecordTransportEvent( const UnitContainer &source,
491                             const Transport &dest, const Unit &u ) {
492   HistEvent *he = new HistEvent;
493 
494   he->type = HIST_TRANSPORT_UNIT;
495   he->data[0] = dest.ID();
496   he->data[1] = u.ID();
497   he->data[2] = he->data[3] = -1;
498   events.AddTail( he );
499   return 0;
500 }
501 
502 ////////////////////////////////////////////////////////////////////////
503 // NAME       : History::ReplayMoveEvent
504 // DESCRIPTION: Show the movement of a unit from one hex to another,
505 //              neighboring hex.
506 // PARAMETERS : event  - move event
507 //              mapwin - map window
508 // RETURNS    : -
509 ////////////////////////////////////////////////////////////////////////
510 
ReplayMoveEvent(const HistEvent & event,MapWindow * mapwin)511 void History::ReplayMoveEvent( const HistEvent &event, MapWindow *mapwin ) {
512   MapView *mv = mapwin->GetMapView();
513   Map *map = mv->GetMap();
514   Mission *mission = Gam->GetMission();
515 
516   Unit *u = mission->GetUnit( event.data[0] );
517   if ( u ) {
518     if ( u != lastunit ) {
519       if ( lastunit && lastunit->IsTransport() && !map->GetMapObject( lastpos ) ) {
520         // we must adjust the position of carried units manually because
521         // those pseudo-transports can't handle this themselves
522         for ( Unit *carry = static_cast<Unit *>( mission->GetUnits().Head() );
523               carry; carry = static_cast<Unit *>( carry->Next() ) ) {
524           if ( carry->Position() == lastpos )
525              carry->SetPosition( lastunit->Position().x, lastunit->Position().y );
526         }
527       }
528       lastunit = u;
529       lastpos = u->Position();
530 
531       SDL_Delay( delay );
532     }
533 
534     mapwin->DisplayHex( u->Position() );
535 
536     if ( u->IsSheltered() ) {
537       if ( map->GetUnit( u->Position() ) ) u->UnsetFlags( U_SHELTERED );
538       else map->GetBuilding( u->Position() )->RemoveUnit( u );
539     } else {
540       map->SetUnit( NULL, u->Position() );
541       mv->UpdateHex( u->Position() );
542     }
543 
544     Gam->MoveUnit( u, (Direction)event.data[1] );
545     map->SetUnit( u, u->Position() );
546     if ( mv->Enabled() ) mapwin->Show( mv->UpdateHex( u->Position() ) );
547   }
548 }
549 
550 ////////////////////////////////////////////////////////////////////////
551 // NAME       : History::ReplayAttackEvent
552 // DESCRIPTION: Show a unit targetting another one.
553 // PARAMETERS : event  - attack event
554 //              mapwin - map window
555 // RETURNS    : -
556 ////////////////////////////////////////////////////////////////////////
557 
ReplayAttackEvent(const HistEvent & event,MapWindow * mapwin)558 void History::ReplayAttackEvent( const HistEvent &event, MapWindow *mapwin ) {
559   MapView *mv = mapwin->GetMapView();
560   Unit *u = Gam->GetMission()->GetUnit( event.data[0] );
561   if ( u ) {
562 
563     if ( u != lastunit ) SDL_Delay( delay );
564     if ( !mv->HexVisible( u->Position() ) ||
565          !mv->HexVisible( Point(event.data[1], event.data[2]) ) ) {
566       mv->CenterOnHex( Point((u->Position().x + event.data[1]) / 2,
567                              (u->Position().y + event.data[2]) / 2) );
568       mapwin->Show( *mv );
569     }
570 
571     Point target( event.data[1], event.data[2]);
572     mv->SetCursorImage( IMG_CURSOR_HIGHLIGHT );
573     Rect upd = mv->SetCursor( u->Position() );
574     mapwin->Show( upd );
575     mv->SetCursorImage( IMG_CURSOR_ATTACK );
576     upd = mv->SetCursor( target );
577     mapwin->Show( upd );
578     mapwin->FlashUnit ( target, 2 );
579     mv->SetCursor( Point(-1,-1) );
580     upd = mv->UpdateHex( u->Position() );
581     mapwin->Show( upd );
582     upd = mv->UpdateHex( target );
583     mapwin->Show( upd );
584     lastunit = u;
585   }
586 }
587 
588 ////////////////////////////////////////////////////////////////////////
589 // NAME       : History::ReplayMessageEvent
590 // DESCRIPTION: Display a message.
591 // PARAMETERS : event - message event
592 //              view  - current view
593 // RETURNS    : -
594 ////////////////////////////////////////////////////////////////////////
595 
ReplayMessageEvent(const HistEvent & event,View * view)596 void History::ReplayMessageEvent( const HistEvent &event, View *view ) {
597   MapWindow *mwin = Gam->GetMapWindow();
598   Mission *m = Gam->GetMission();
599 
600   if ( mwin->GetMapView()->Enabled() && (event.data[2] >= 0) )
601     mwin->DisplayHex( m->GetMap().Index2Hex( event.data[2] ) );
602 
603   MessageWindow *msgw = new MessageWindow(
604     (event.data[0] != -1) ? m->GetMessage( event.data[0] ) : MSG(MSG_MESSAGE),
605     m->GetMessage( event.data[1] ), view );
606   msgw->EventLoop();
607   view->CloseWindow( msgw );
608 }
609 
610 ////////////////////////////////////////////////////////////////////////
611 // NAME       : History::ReplayTileEvent
612 // DESCRIPTION: Replay a tile switch.
613 // PARAMETERS : event  - tile event
614 //              mapwin - map window
615 // RETURNS    : -
616 ////////////////////////////////////////////////////////////////////////
617 
ReplayTileEvent(const HistEvent & event,MapWindow * mapwin)618 void History::ReplayTileEvent( const HistEvent &event, MapWindow *mapwin ) {
619   MapView *mv = mapwin->GetMapView();
620   Map *map = mv->GetMap();
621   Point pos( event.data[1], event.data[2] );
622 
623   if ( mv->Enabled() ) {
624     const TerrainType *tt = Gam->GetMission()->GetTerrainSet().GetTerrainInfo( event.data[0] );
625     mapwin->DisplayHex( pos );
626     SDL_Delay( delay );
627     mapwin->FadeInTerrain( tt->tt_image, pos );
628     SDL_Delay( delay );
629   }
630   map->SetHexType( event.data[1], event.data[2], event.data[0] );
631 }
632 
633 ////////////////////////////////////////////////////////////////////////
634 // NAME       : History::ReplayCombatEvent
635 // DESCRIPTION: Replay a fight switch.
636 // PARAMETERS : event  - combat event
637 //              mapwin - map window
638 // RETURNS    : -
639 ////////////////////////////////////////////////////////////////////////
640 
ReplayCombatEvent(const HistEvent & event,MapWindow * mapwin)641 void History::ReplayCombatEvent( const HistEvent &event, MapWindow *mapwin ) {
642 
643   // for the quick replay we may have to activate the display now
644   MapView *mv = mapwin->GetMapView();
645   if (!mv->Enabled()) {
646     delay = DEFAULT_DELAY;
647     mv->Enable();
648     mapwin->Draw();
649     mapwin->Show();
650   }
651 
652   Combat cmb( Gam->GetMission()->GetUnit( event.data[0] ),
653               Gam->GetMission()->GetUnit( event.data[1] ) );
654   Point casualties( event.data[3], event.data[2] );
655   Gam->ResolveBattle( &cmb, &casualties );
656 }
657 
658 ////////////////////////////////////////////////////////////////////////
659 // NAME       : History::ReplayUnitEvent
660 // DESCRIPTION: Add a new unit to the map.
661 // PARAMETERS : event  - unit event
662 //              mapwin - map window
663 // RETURNS    : -
664 ////////////////////////////////////////////////////////////////////////
665 
ReplayUnitEvent(const HistEvent & event,MapWindow * mapwin)666 void History::ReplayUnitEvent( const HistEvent &event, MapWindow *mapwin ) {
667   Mission *mission = Gam->GetMission();
668   MapView *mv = mapwin->GetMapView();
669   Map *map = mv->GetMap();
670 
671   Unit *u = GetDummy( event.data[0] );
672   if ( u ) {
673     if ( event.data[1] == HIST_UEVENT_CREATE ) { // create unit
674       u->Remove();
675 
676       if ( !u->IsSheltered() ) {
677         if ( mv->Enabled() ) {
678           mapwin->DisplayHex( u->Position() );
679           SDL_Delay( delay );
680           mapwin->FadeInUnit( u->Image(), u->Position() );
681           SDL_Delay( delay );
682         }
683         map->SetUnit( u, u->Position() );
684       }
685       mission->GetUnits().AddTail( u );
686     } else if ( event.data[1] == HIST_UEVENT_DESTROY ) { // destroy unit
687       if ( !u->IsSheltered() ) {
688         map->SetUnit( NULL, u->Position() );
689         if ( mv->Enabled() ) {
690           mapwin->DisplayHex( u->Position() );
691           SDL_Delay( delay );
692           mapwin->FadeOutUnit( u->Image(), u->Position() );
693           SDL_Delay( delay );
694         }
695       }
696     }
697   }
698 }
699 
700 ////////////////////////////////////////////////////////////////////////
701 // NAME       : History::UndoMove
702 // DESCRIPTION: Erase all recorded movement and related events for a
703 //              specific unit.
704 // PARAMETERS : u - unit to erase movement for
705 // RETURNS    : -
706 ////////////////////////////////////////////////////////////////////////
707 
UndoMove(const Unit & u)708 void History::UndoMove( const Unit &u ) {
709   HistEvent *he = static_cast<HistEvent *>( events.Head() ), *tmp;
710 
711   while ( he ) {
712     tmp = static_cast<HistEvent *>( he->Next() );
713 
714     if ( ((he->type == HIST_MOVE) ||
715           (he->type == HIST_TRANSPORT_CRYSTALS) ||
716           (he->type == HIST_TRANSPORT_UNIT)) &&
717          (he->data[0] == u.ID()) ) {
718       he->Remove();
719       delete he;
720     }
721 
722     he = tmp;
723   }
724 }
725 
726 ////////////////////////////////////////////////////////////////////////
727 // NAME       : History::GetDummy
728 // DESCRIPTION: Retrieve an internal unit definition.
729 // PARAMETERS : id - unit identifier
730 // RETURNS    : dummy for the requested unit, NULL if not found
731 ////////////////////////////////////////////////////////////////////////
732 
GetDummy(unsigned short id) const733 Unit *History::GetDummy( unsigned short id ) const {
734   Unit *u;
735   for ( u = static_cast<Unit *>( units.Head() );
736         u && u->ID() != id; u = static_cast<Unit *>( u->Next() ) );
737   return u;
738 }
739 
740 ////////////////////////////////////////////////////////////////////////
741 // NAME       : History::SetEventsProcessed
742 // DESCRIPTION: Set all events to processed.
743 // PARAMETERS : -
744 // RETURNS    : -
745 ////////////////////////////////////////////////////////////////////////
746 
SetEventsProcessed(void) const747 void History::SetEventsProcessed( void ) const {
748   for ( HistEvent *he = static_cast<HistEvent *>( events.Head() );
749         he; he = static_cast<HistEvent *>( he->Next() ) ) {
750     he->processed = true;
751   }
752 }
753