2
* Copyright (C) 2002-2004 by The Widelands Development Team
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License
6
* as published by the Free Software Foundation; either version 2
7
* of the License, or (at your option) any later version.
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
21
#include "animation.h"
23
#include "critter_bob.h"
28
#include "rendertarget.h"
29
#include "transport.h"
31
#include "wexception.h"
35
==============================================================================
39
==============================================================================
48
Bob_Descr::Bob_Descr(const char *name, Tribe_Descr* owner_tribe)
50
snprintf(m_name, sizeof(m_name), "%s", name);
51
m_default_encodedata.clear();
52
m_owner_tribe=owner_tribe;
55
Bob_Descr::~Bob_Descr(void)
64
Parse additional information from the config file
67
void Bob_Descr::parse(const char *directory, Profile *prof, const EncodeData *encdata)
72
Section* global = prof->get_safe_section("idle");
75
snprintf(buf, sizeof(buf), "%s_00.png", m_name);
76
snprintf(picname, sizeof(picname), "%s/%s", directory, global->get_string("picture", buf));
79
m_default_encodedata.parse(global);
81
snprintf(picname, sizeof(picname), "%s_??.png", m_name);
82
add_animation("idle", g_anim.get(directory, prof->get_safe_section("idle"), picname, encdata));
86
global= prof->get_safe_section("global");
87
while(global->get_next_string("attrib", &string)) {
88
uint attrib = get_attribute_id(string);
90
if (attrib < Map_Object::HIGHEST_FIXED_ATTRIBUTE)
92
throw wexception("Bad attribute '%s'", string);
95
add_attribute(attrib);
104
Create a bob of this type
107
Bob *Bob_Descr::create(Editor_Game_Base *gg, Player *owner, Coords coords)
109
Bob *bob = create_object();
110
bob->set_owner(owner);
111
bob->set_position(gg, coords);
119
==============================
123
==============================
130
Zero-initialize a map object
133
Bob::Bob(Bob_Descr* descr)
137
m_position.x = m_position.y = 0; // not linked anywhere
138
m_position.field = 0;
142
m_actid = 0; // this isn't strictly necessary, but it shuts up valgrind and "feels" cleaner
148
m_walkstart = m_walkend = 0;
150
m_stack_dirty = false;
151
m_sched_init_task = false;
159
Cleanup an object. Removes map links
164
if (m_position.field) {
165
molog("Map_Object::~Map_Object: m_pos.field != 0, cleanup() not called!\n");
186
Bobs have a call-stack of "tasks". The top-most task is the one that is
187
currently being executed. As soon as the task is finished (either successfully
188
or with an error signal), it is popped from the top of the stack, and the task
189
that is now on top will be asked to update() itself.
191
Upon initialization, an object has no task at all. A CMD_ACT will be scheduled
192
automatically. When it is executed, init_auto_task() is called to automatically
193
select a fallback task.
194
However, the creator of the bob can choose to push a specific task immediately
195
after creating the bob. This will override the fallback behaviour.
196
init_auto_task() is also called when the final task is popped from the stack.
198
All state information that a task uses must be stored in the State structure
199
returned by get_state(). Note that every task on the task stack has its own
200
state structure, i.e. push_task() does not destroy the current task's state.
202
In order to start a new sub-task, you have to call push_task(), and then fill
203
the State structure returned by get_state() with any parameters that the task
205
A task is ended by pop_task(). Note, however, that you should only call
206
pop_task() from a task's update() or signal() function.
207
If you want to interrupt the current task for some reason, you should call
210
To implement a new task, you need to create a new Task object, and at least an
211
update() function. The signal() and mask() functions are optional (can be 0).
212
The update() function is called once after the task is pushed, and whenever a
213
previously scheduled CMD_ACT occured. It is also called when a sub-task is pops
215
update() must call either schedule_act() or skip_act() if you really don't
216
want a CMD_ACT to occur. Note that if you call skip_act(), your task MUST
218
Alternatively, update() can call pop_task() to end the current task.
220
signal() is usually called by send_signal().
221
signal() is also called when a sub-task returns while a signal is still set.
222
Note that after signal() called, update() is also called.
223
signal() must call schedule_act(), or pop_task(), or do nothing at all.
224
If signal() is not implemented, it is equivalent to a signal() function that
227
Whenever send_signal() is called, the mask() function of all tasks are called,
228
starting with the highest-level task.
229
The mask() function cannot schedule CMD_ACT, but it can use set_signal() to
230
modify or even clear the signal before it reaches the normal signal handling
238
Make sure you call this from derived classes!
240
Initialize the object
243
void Bob::init(Editor_Game_Base* gg)
245
Map_Object::init(gg);
247
m_sched_init_task = true;
250
Game* g=static_cast<Game*>(gg);
254
// In editor: play idle task forever
255
set_animation(gg, get_descr()->get_animation("idle"));
264
Perform independant cleanup as necessary.
267
void Bob::cleanup(Editor_Game_Base *gg)
269
while(m_stack.size()) {
271
m_stack_dirty = false;
274
if (m_position.field) {
275
m_position.field = 0;
276
*m_linkpprev = m_linknext;
278
m_linknext->m_linkpprev = m_linkpprev;
281
Map_Object::cleanup(gg);
289
Called by Cmd_Queue when a CMD_ACT event is triggered.
290
Hand the acting over to the task.
292
Change to the next task if necessary.
295
void Bob::act(Game* g, uint data)
297
if (!m_stack.size()) {
298
assert(m_sched_init_task);
300
m_sched_init_task = false;
303
m_stack_dirty = false;
305
m_sched_init_task = false;
308
throw wexception("MO(%u): init_auto_task() failed to set a task", get_serial());
310
assert(m_stack_dirty);
312
m_stack_dirty = false;
315
} else if (m_sched_init_task) {
316
assert(m_stack_dirty);
318
// This happens when we schedule for an init_auto_task(), but the bob
319
// owner explicitly pushes a task
320
m_sched_init_task = false;
321
m_stack_dirty = false;
324
// Eliminate spurious calls of act().
325
// These calls are to be expected and perfectly normal, e.g. when a carrier's
326
// idle task is interrupted by the request to pick up a ware from a flag.
338
Handle the actual calls to update() and signal() as appropriate.
340
signalhandling is true when do_act() is called by send_signal(), and false
344
void Bob::do_act(Game* g, bool signalhandling)
352
throw wexception("MO(%u): stack dirty before update[%s]", get_serial(),
353
get_state() ? get_state()->task->name : "(nil)");
355
// Run the task if we're not coming from signalhandling
356
if (!signalhandling) {
357
Task* task = get_state()->task;
359
(this->*task->update)(g, get_state());
363
if (origactid == m_actid)
364
throw wexception("MO(%u): update[%s] failed to act", get_serial(), task->name);
366
break; // we did our work, now get out of here
368
else if (origactid != m_actid)
369
throw wexception("MO(%u): [%s] changes both stack and act", get_serial(),
375
m_stack_dirty = false;
377
// Get a new task as soon as possible
378
// Sometimes, the owner of a bob will want to force it to stop all
379
// tasks and start a new task afterwards, which is why we don't
380
// call init_auto_task() immediately.
381
if (!m_stack.size()) {
382
molog("schedule reget auto task\n");
386
m_sched_init_task = true;
390
// Prepare the new task
393
Task* task = get_state()->task;
396
(this->*task->signal)(g, get_state());
398
// If the initial signal handler doesn't mess with the stack, get out of here
399
if (!m_stack_dirty && signalhandling)
402
} while(m_stack_dirty);
404
signalhandling = false; // next pass will be a normal, non-signal handling pass
411
Bob::schedule_destroy
416
void Bob::schedule_destroy(Game* g)
418
Map_Object::schedule_destroy(g);
427
Schedule a new act for the current task. All other pending acts are cancelled.
430
void Bob::schedule_act(Game* g, uint tdelta)
432
Map_Object::schedule_act(g, tdelta, ++m_actid);
440
Explicitly state that we don't want to act.
443
void Bob::skip_act(Game* g)
445
if (!get_state()->task->signal)
446
throw wexception("MO(%u): %s calls skip_act(), but has no signal() function",
447
get_serial(), get_state()->task->name);
457
Explicitly state that we don't want to act, even if we cannot be awoken by a
458
signal. Use with great care.
461
void Bob::force_skip_act(Game* g)
463
m_stack_dirty = false;
472
Push a new task onto the stack.
474
push_task() itself does not call any functions of the task, so the caller can
475
fill the state information with parameters for the task.
478
void Bob::push_task(Game* g, Task* task)
482
if (m_stack_dirty && m_stack.size())
483
throw wexception("MO(%u): push_task(%s): stack already dirty", get_serial(), task->name);
485
m_stack.push_back(State());
497
m_stack_dirty = true;
505
Remove the current task from the stack.
507
pop_task() itself does not call any parent task functions, but it sets a flag
511
void Bob::pop_task(Game* g)
513
State* state = get_state();
516
throw wexception("MO(%u): pop_task(%s): stack already dirty", get_serial(), state->task->name);
523
state->transfer->has_failed();
528
m_stack_dirty = true;
536
Get the bottom-most (usually the only) state of this task from the stack.
537
Return 0 if this task is not running at all.
540
Bob::State* Bob::get_state(Task* task)
542
std::vector<State>::iterator it = m_stack.end();
544
while(it != m_stack.begin())
548
if (it->task == task)
560
Sets the signal string and calls the current task's signal function, if any.
563
void Bob::send_signal(Game* g, std::string sig)
565
assert(sig.size()); // use set_signal() for signal removal
569
for(uint i = 0; i < m_stack.size(); i++) {
570
State* state = &m_stack[i];
572
if (state->task->mask) {
573
(this->*state->task->mask)(g, state);
575
if (!m_signal.size())
580
do_act(g, true); // signal handler act
588
Force a complete reset of the state stack.
589
The state stack is emptied completely, and an init auto task is scheduled
590
as if the Bob has just been created and initialized.
593
void Bob::reset_tasks(Game* g)
595
while(m_stack.size()) {
596
m_stack_dirty = false;
603
m_sched_init_task = true;
604
m_stack_dirty = false;
612
Simply set the signal string without calling any functions.
613
You should use this function to unset a signal, or to set a signal just before
617
void Bob::set_signal(std::string sig)
627
Automatically select a task.
630
void Bob::init_auto_task(Game* g)
636
==============================
640
Wait a time or indefinitely.
641
Every signal can interrupt this task. No signals are caught.
643
==============================
646
Bob::Task Bob::taskIdle = {
659
Start an idle phase, using the given animation
660
If the timeout is a positive value, the idle phase stops after the given
663
This task always succeeds unless interrupted.
666
void Bob::start_task_idle(Game* g, uint anim, int timeout)
670
assert(timeout < 0 || timeout > 0);
672
set_animation(g, anim);
674
push_task(g, &taskIdle);
677
state->ivar1 = timeout;
680
void Bob::idle_update(Game* g, State* state)
687
if (state->ivar1 > 0)
688
schedule_act(g, state->ivar1);
695
void Bob::idle_signal(Game* g, State* state)
702
==============================
706
Move along a predefined path.
707
ivar1 is the step number.
708
ivar2 is non-zero if we should force moving onto the final field.
709
ivar3 is number of steps to take maximally or -1
711
Sets the following signal(s):
712
"fail" - cannot move along the given path
714
==============================
717
Bob::Task Bob::taskMovepath = {
720
&Bob::movepath_update,
721
&Bob::movepath_signal, // lazy signal handling, only act on one signal
727
Bob::start_task_movepath
729
Start moving to the given destination. persist is the same parameter as
732
Returns false if no path could be found.
734
The task finishes once the goal has been reached. It may fail.
736
only step defines how many steps should be taken, before this returns as a success
739
bool Bob::start_task_movepath(Game* g, Coords dest, int persist, DirAnimations *anims, bool forceonlast, int only_step)
741
Path* path = new Path;
743
CheckStepDefault cstep_default(get_movecaps());
744
CheckStepWalkOn cstep_walkon(get_movecaps(), true);
748
cstep = &cstep_walkon;
750
cstep = &cstep_default;
752
if (g->get_map()->findpath(m_position, dest, persist, path, cstep) < 0) {
757
push_task(g, &taskMovepath);
761
state->ivar1 = 0; // step #
762
state->ivar2 = forceonlast ? 1 : 0;
763
state->ivar3 = only_step;
764
state->diranims = anims;
772
Bob::start_task_movepath
774
Start moving along the given, precalculated path.
777
void Bob::start_task_movepath(Game* g, const Path &path, DirAnimations *anims, bool forceonlast, int only_step)
781
assert(path.get_start() == get_position());
783
push_task(g, &taskMovepath);
786
state->path = new Path(path);
788
state->ivar2 = forceonlast ? 1 : 0;
789
state->ivar3 = only_step;
790
state->diranims = anims;
796
Bob::start_task_movepath
798
Move to the given index on the given path. The current position must be on the
801
Return true if a task has been started, or false if we already are on the given
805
bool Bob::start_task_movepath(Game* g, const Path& origpath, int index, DirAnimations* anims, bool forceonlast, int only_step)
807
CoordPath path(origpath);
808
int curidx = path.get_index(get_position());
811
throw wexception("MO(%u): start_task_movepath(index): not on path", get_serial());
813
if (curidx != index) {
814
molog("Bob::walk_to_index: Move from %i to %i.\n", curidx, index);
816
if (curidx < index) {
817
path.truncate(index);
818
path.starttrim(curidx);
820
start_task_movepath(g, path, anims, forceonlast, only_step);
822
path.truncate(curidx);
823
path.starttrim(index);
826
start_task_movepath(g, path, anims, forceonlast, only_step);
841
void Bob::movepath_update(Game* g, State* state)
846
// We ignore signals when they arrive, but interrupt as soon as possible,
847
// i.e. when the next step has finished
848
if (get_signal().size()) {
849
molog("[movepath]: Interrupted by signal '%s'.\n", get_signal().c_str());
854
if (!state->path || state->ivar1 >= state->path->get_nsteps()) {
855
assert(!state->path || m_position == state->path->get_end());
856
pop_task(g); // success
858
} else if(state->ivar1==state->ivar3) {
859
// We have stepped all steps that we were asked for.
860
// This is some kind of success, though we do not are were we wanted
866
char dir = state->path->get_step(state->ivar1);
867
bool forcemove = false;
869
if (state->ivar2 && (state->ivar1+1) == state->path->get_nsteps())
872
int tdelta = start_walk(g, (WalkingDir)dir, state->diranims->get_animation(dir), forcemove);
874
molog("[movepath]: Can't walk.\n");
875
set_signal("fail"); // failure to reach goal
882
schedule_act(g, tdelta);
888
void Bob::movepath_signal(Game* g, State* start) {
889
if(get_signal()=="interrupt_now") {
890
// User requests an immediate interrupt.
891
// Well, he better nows what he's doing
897
==============================
901
==============================
904
Bob::Task Bob::taskForcemove = {
907
&Bob::forcemove_update,
915
Bob::start_task_forcemove
917
Move into the given direction, without passability checks.
920
void Bob::start_task_forcemove(Game *g, int dir, DirAnimations *anims)
924
push_task(g, &taskForcemove);
928
state->diranims = anims;
931
void Bob::forcemove_update(Game* g, State* state)
935
int tdelta = start_walk(g, (WalkingDir)state->ivar1,
936
state->diranims->get_animation(state->ivar1), true);
939
schedule_act(g, tdelta);
953
Calculates the actual position to draw on from the base field position.
954
This function takes walking etc. into account.
956
pos is the location, in pixels, of the field m_position (height is already
958
drawpos will be filled with the location of the bob.
961
void Bob::calc_drawpos(Editor_Game_Base* game, Point pos, Point* drawpos)
963
Map *map = game->get_map();
974
case WALK_NW: map->get_brn(end, &start); spos.x += FIELD_WIDTH/2; spos.y += FIELD_HEIGHT/2; break;
975
case WALK_NE: map->get_bln(end, &start); spos.x -= FIELD_WIDTH/2; spos.y += FIELD_HEIGHT/2; break;
976
case WALK_W: map->get_rn(end, &start); spos.x += FIELD_WIDTH; break;
977
case WALK_E: map->get_ln(end, &start); spos.x -= FIELD_WIDTH; break;
978
case WALK_SW: map->get_trn(end, &start); spos.x += FIELD_WIDTH/2; spos.y -= FIELD_HEIGHT/2; break;
979
case WALK_SE: map->get_tln(end, &start); spos.x -= FIELD_WIDTH/2; spos.y -= FIELD_HEIGHT/2; break;
981
case IDLE: start.field = 0; break;
985
spos.y += MULTIPLY_WITH_HEIGHT_FACTOR(end.field->get_height());
986
spos.y -= MULTIPLY_WITH_HEIGHT_FACTOR(start.field->get_height());
988
float f = (float)(game->get_gametime() - m_walkstart) / (m_walkend - m_walkstart);
990
else if (f > 1) f = 1;
992
epos.x = (int)(f*epos.x + (1-f)*spos.x);
993
epos.y = (int)(f*epos.y + (1-f)*spos.y);
1004
Draw the map object.
1005
posx/posy is the on-bitmap position of the field we're currently on.
1007
It LERPs between start and end position when we're walking.
1008
Note that the current field is actually the field we're walking to, not
1009
the one we start from.
1012
void Bob::draw(Editor_Game_Base *game, RenderTarget* dst, Point pos)
1017
const RGBColor* playercolors = 0;
1020
calc_drawpos(game, pos, &drawpos);
1023
playercolors = get_owner()->get_playercolor();
1025
dst->drawanim(drawpos.x, drawpos.y, m_anim, game->get_gametime() - m_animstart, playercolors);
1033
Set a looping animation, starting now.
1036
void Bob::set_animation(Editor_Game_Base* g, uint anim)
1039
m_animstart = g->get_gametime();
1046
Return true if we're currently walking
1049
bool Bob::is_walking()
1051
return m_walking != IDLE;
1058
Call this from your task_act() function that was scheduled after start_walk().
1061
void Bob::end_walk(Game* g)
1071
Cause the object to walk, honoring passable/impassable parts of the map using movecaps.
1072
If force is true, the passability check is skipped.
1074
Returns the number of milliseconds after which the walk has ended. You must
1075
call end_walk() after this time, so schedule a task_act().
1077
Returns a negative value when we can't walk into the requested direction.
1080
int Bob::start_walk(Game *g, WalkingDir dir, uint a, bool force)
1084
g->get_map()->get_neighbour(m_position, dir, &newf);
1086
// Move capability check by ANDing with the field caps
1088
// The somewhat crazy check involving MOVECAPS_SWIM should allow swimming objects to
1089
// temporarily land.
1090
uint movecaps = get_movecaps();
1093
if (!(m_position.field->get_caps() & movecaps & MOVECAPS_SWIM && newf.field->get_caps() & MOVECAPS_WALK) &&
1094
!(newf.field->get_caps() & movecaps))
1099
int tdelta = g->get_map()->calc_cost(m_position, dir);
1102
m_walkstart = g->get_gametime();
1103
m_walkend = m_walkstart + tdelta;
1105
set_position(g, newf);
1106
set_animation(g, a);
1108
return tdelta; // yep, we were successful
1115
Moves the Map_Object to the given position.
1118
void Bob::set_position(Editor_Game_Base* g, Coords coords)
1120
if (m_position.field) {
1121
*m_linkpprev = m_linknext;
1123
m_linknext->m_linkpprev = m_linkpprev;
1126
m_position = g->get_map()->get_fcoords(coords);
1128
m_linknext = m_position.field->bobs;
1129
m_linkpprev = &m_position.field->bobs;
1131
m_linknext->m_linkpprev = &m_linknext;
1132
m_position.field->bobs = this;
1135
* Give debug informations
1137
void Bob::log_general_info(Editor_Game_Base* egbase) {
1138
molog("Owner: %p\n", m_owner);
1140
molog("Postition: (%i,%i)\n", m_position.x, m_position.y);
1141
molog("ActID: %i\n", m_actid);
1143
molog("Animation: %s\n", m_anim ? get_descr()->get_animation_name(m_anim).c_str() : "<none>" );
1144
molog("AnimStart: %i\n", m_animstart);
1146
molog("WalkingDir: %i\n", m_walking);
1147
molog("WalkingStart: %i\n", m_walkstart);
1148
molog("WalkEnd: %i\n", m_walkend);
1150
molog("Stack dirty: %i\n", m_stack_dirty);
1151
molog("Init auto task: %i\n", m_sched_init_task);
1152
molog("Signale: %s\n", m_signal.c_str());
1154
molog("Stack size: %i\n", m_stack.size());
1155
for(uint i=0; i<m_stack.size(); i++) {
1156
molog("Stack dump %i/%i\n", i+1, m_stack.size());
1158
molog("* task->name: %s\n", m_stack[i].task->name);
1160
molog("* ivar1: %i\n", m_stack[i].ivar1);
1161
molog("* ivar2: %i\n", m_stack[i].ivar2);
1162
molog("* ivar3: %i\n", m_stack[i].ivar3);
1164
molog("* object pointer: %p\n", m_stack[i].objvar1.get(egbase));
1165
molog("* svar1: %s\n", m_stack[i].svar1.c_str());
1167
molog("* coords: (%i,%i)\n", m_stack[i].coords.x, m_stack[i].coords.y);
1168
molog("* diranims: %p\n", m_stack[i].diranims);
1169
molog("* path: %p\n", m_stack[i].path);
1170
if(m_stack[i].path) {
1171
Path* p=m_stack[i].path;
1172
molog("** Path length: %i\n", p->get_nsteps());
1173
molog("** Start: (%i,%i)\n", p->get_start().x, p->get_start().y);
1174
molog("** End: (%i,%i)\n", p->get_end().x, p->get_end().y);
1175
for(int i=0; i<p->get_nsteps(); i++)
1176
molog("** Step %i/%i: %i\n", i+1, p->get_nsteps(), p->get_step(i));
1178
molog("* transfer: %p\n", m_stack[i].transfer);
1179
molog("* route: %p\n", m_stack[i].route);
1181
molog("* program: %p\n", m_stack[i].route);
1186
==============================================================================
1190
==============================================================================
1195
Bob_Descr::create_from_dir(const char *directory) [static]
1197
Master factory to read a bob from the given directory and create the
1198
appropriate description class.
1201
Bob_Descr *Bob_Descr::create_from_dir(const char *name, const char *directory, Profile *prof, Tribe_Descr* tribe)
1207
Section *s = prof->get_safe_section("global");
1208
const char *type = s->get_safe_string("type");
1210
if (!strcasecmp(type, "critter")) {
1211
bob = new Critter_Bob_Descr(name, tribe);
1213
throw wexception("Unsupported bob type '%s'", type);
1215
bob->parse(directory, prof, 0);
1217
catch(std::exception &e) {
1220
throw wexception("Error reading bob %s: %s", directory, e.what());