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