2 Copyright (C) 1998 BJ Eirich (aka vecna)
\r
3 This program is free software; you can redistribute it and/or
\r
4 modify it under the terms of the GNU General Public License
\r
5 as published by the Free Software Foundation; either version 2
\r
6 of the License, or (at your option) any later version.
\r
7 This program is distributed in the hope that it will be useful,
\r
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
\r
10 See the GNU General Public Lic
\r
11 See the GNU General Public License for more details.
\r
12 You should have received a copy of the GNU General Public License
\r
13 along with this program; if not, write to the Free Software
\r
14 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
\r
17 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
\r
20 // + corrected oversight in movement script management by sticking a hack in
\r
21 // MoveScript(). Bug caused Fx commands to not work sometimes.
\r
22 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
\r
27 // ================================= Data ====================================
\r
31 int x, y; // xwc, ywx position
\r
32 word tx, ty; // xtc, ytc position
\r
33 byte facing; // direction entity is facing
\r
34 byte moving, movecnt; // direction entity is moving
\r
35 byte frame; // bottom-line frame to display
\r
36 byte specframe; // special-frame set thingo
\r
37 byte chrindex, reset; // CHR index | Reset animation
\r
38 byte obsmode1, obsmode2; // can be obstructed | Is an obstruction
\r
39 byte speed, speedct; // entity speed, speedcount :)
\r
40 byte delayct; // animation frame-delay
\r
41 char *animofs, *scriptofs; // anim script | move script
\r
42 byte face, actm; // auto-face | activation mode
\r
43 byte movecode, movescript; // movement type | movement script
\r
44 byte ctr, mode; // sub-tile move ctr, mode flag (internal)
\r
45 word step, delay; // step, delay
\r
46 word stepctr, delayctr; // internal use counters
\r
47 word data1, data2, data3; //
\r
48 word data4, data5, data6; //
\r
49 int actscript; // activation script
\r
50 int expand1, expand2; //
\r
51 int expand3, expand4; //
\r
52 char desc[20]; // Entity description.
\r
57 byte *imagedata; // CHR frame data
\r
58 int fxsize, fysize; // frame x/y dimensions
\r
59 int hx, hy; // x/y obstruction hotspot
\r
60 int totalframes; // total # of frames.
\r
74 chrlist_r chrlist[100];
\r
75 byte nmchr,playernum;
\r
77 entity_r entity[256];
\r
81 byte entidx[256], cc;
\r
84 // ================================= Code ====================================
\r
86 int ObstructionAt(int tx, int ty)
\r
88 if (obstruct[(ty*layer[0].sizex)+tx]) return 1;
\r
89 if (tx==-1 || ty==-1) return 1;
\r
90 if (tx==layer[0].sizex || ty==layer[0].sizey) return 1;
\r
94 int Zone(int tx, int ty)
\r
96 return zone[(ty*layer[0].sizex)+tx];
\r
99 void LoadCHR(char *fname, chrdata *c)
\r
106 if (!f) err("Could not open CHR file %s.",fname);
\r
108 if (b!=2) err("CHR %s incorrect CHR format version.",fname);
\r
109 vread(&c->fxsize, 2, f);
\r
110 vread(&c->fysize, 2, f);
\r
111 vread(&c->hx, 2, f);
\r
112 vread(&c->hy, 2, f);
\r
113 vread(strbuf, 4, f); // skip the hotspot size.
\r
114 vread(&c->totalframes, 2, f);
\r
117 ptr=(char *) valloc(i, "LoadCHR:ptr", OID_TEMP);
\r
119 c->imagedata=(byte *) valloc(c->fxsize * c->fysize * c->totalframes, "LoadCHR:c->imagedata", OID_IMAGE);
\r
120 ReadCompressedLayer1(c->imagedata, c->fxsize * c->fysize * c->totalframes, ptr);
\r
124 vread(&c->lidle, 4, f);
\r
125 vread(&c->ridle, 4, f);
\r
126 vread(&c->uidle, 4, f);
\r
127 vread(&c->didle, 4, f);
\r
129 for (b=0; b<4; b++)
\r
133 case 0: ptr=c->lanim; break;
\r
134 case 1: ptr=c->ranim; break;
\r
135 case 2: ptr=c->uanim; break;
\r
136 case 3: ptr=c->danim; break;
\r
139 if (i>100) err("Animation strand too long. %d",i);
\r
145 int CacheCHR(char *fname)
\r
147 LoadCHR(fname, &chr[numchrs]);
\r
149 return (numchrs-1);
\r
156 for (i=0; i<numchrs; i++)
\r
157 vfree(chr[i].imagedata);
\r
158 memset(chr, i, sizeof chr);
\r
165 for (i=0; i<nmchr; i++)
\r
166 if (strlen(chrlist[i].t)) CacheCHR(chrlist[i].t);
\r
169 void DrawEntity(int i)
\r
173 dx=entity[i].x-xwin;
\r
174 dy=entity[i].y-ywin;
\r
175 a=entity[i].chrindex;
\r
176 if (a>=numchrs) return;
\r
177 b=entity[i].specframe ? entity[i].specframe : entity[i].frame;
\r
178 TCopySpriteClip(dx-chr[a].hx, dy-chr[a].hy, chr[a].fxsize, chr[a].fysize,
\r
179 (byte *) (chr[a].imagedata+(b*chr[a].fxsize*
\r
183 static int cmpent(const void* a, const void* b)
\r
185 return entity[*(byte*)a].y - entity[*(byte*)b].y;
\r
188 void RenderEntities()
\r
192 qsort(entidx, cc, 1, cmpent);
\r
193 for (i=0; i<cc; i++) DrawEntity(entidx[i]);
\r
196 int GetArg(entity_r *p)
\r
199 static char token[10];
\r
202 while (*p->animofs==' ') p->animofs++;
\r
203 while (*p->animofs>=48 && *p->animofs<=57)
\r
205 token[j]=*p->animofs;
\r
210 return atoi(token);
\r
213 void GetNextCommand(entity_r *p)
\r
217 while (*p->animofs==' ') p->animofs++;
\r
222 case 'F': p->frame=GetArg(p); break;
\r
223 case 'W': p->delayct=GetArg(p); break;
\r
224 case 0: if (p->moving)
\r
227 case 0: p->animofs=chr[p->chrindex].danim; break;
\r
228 case 1: p->animofs=chr[p->chrindex].uanim; break;
\r
229 case 2: p->animofs=chr[p->chrindex].lanim; break;
\r
230 case 3: p->animofs=chr[p->chrindex].ranim; break;
\r
235 case 0: p->animofs=0; p->frame=chr[p->chrindex].didle; break;
\r
236 case 1: p->animofs=0; p->frame=chr[p->chrindex].uidle; break;
\r
237 case 2: p->animofs=0; p->frame=chr[p->chrindex].lidle; break;
\r
238 case 3: p->animofs=0; p->frame=chr[p->chrindex].ridle; break;
\r
240 p->delayct=0; break;
\r
244 void AnimateEntity(entity_r *p)
\r
246 if (!p->animofs && p->moving)
\r
250 case 0: p->animofs=chr[p->chrindex].danim; break;
\r
251 case 1: p->animofs=chr[p->chrindex].uanim; break;
\r
252 case 2: p->animofs=chr[p->chrindex].lanim; break;
\r
253 case 3: p->animofs=chr[p->chrindex].ranim; break;
\r
257 if (!p->animofs && !p->moving)
\r
261 case 0: p->animofs=0; p->frame=chr[p->chrindex].didle; break;
\r
262 case 1: p->animofs=0; p->frame=chr[p->chrindex].uidle; break;
\r
263 case 2: p->animofs=0; p->frame=chr[p->chrindex].lidle; break;
\r
264 case 3: p->animofs=0; p->frame=chr[p->chrindex].ridle; break;
\r
272 else GetNextCommand(p);
\r
275 int EntityAt(int ex, int ey)
\r
278 for (i=0; i<cc; i++)
\r
280 if (&entity[entidx[i]]==player) continue;
\r
281 if (ex==entity[entidx[i]].tx && ey==entity[entidx[i]].ty)
\r
282 return entidx[i]+1;
\r
287 int EntityObsAt(int ex, int ey)
\r
290 for (i=0; i<cc; i++)
\r
292 if (&entity[entidx[i]]==player) continue;
\r
293 if (ex==entity[entidx[i]].tx && ey==entity[entidx[i]].ty &&
\r
294 entity[entidx[i]].obsmode2)
\r
295 return entidx[i]+1;
\r
300 int AEntityObsAt(int ex, int ey)
\r
303 for (i=0; i<cc; i++)
\r
305 if (ex==entity[entidx[i]].tx && ey==entity[entidx[i]].ty &&
\r
306 entity[entidx[i]].obsmode2)
\r
307 return entidx[i]+1;
\r
312 void SiftEntities()
\r
315 memset(&entidx, 0, 256); cc=0;
\r
316 for (i=0; i<entities; i++)
\r
318 dx=entity[i].x-xwin+16;
\r
319 dy=entity[i].y-ywin+16;
\r
321 if (dx<0 || dx>sx+chr[entity[i].chrindex].fxsize) continue;
\r
322 if (dy<0 || dy>sy+chr[entity[i].chrindex].fysize) continue;
\r
323 entidx[cc]=i; cc++;
\r
327 void MoveRight(int i)
\r
331 tx=entity[i].tx+1; ty=entity[i].ty;
\r
332 if (entity[i].obsmode1 && (ObstructionAt(tx,ty) || AEntityObsAt(tx,ty)))
\r
333 { movesuccess=0; return; }
\r
334 if (entity[i].facing!=3)
\r
336 entity[i].delayct=0;
\r
337 entity[i].animofs=0;
\r
339 entity[i].x++; entity[i].facing=3; entity[i].moving=4; entity[i].ctr=15;
\r
340 entity[i].tx++; movesuccess=1; entity[i].reset=0;
\r
343 void MoveLeft(int i)
\r
347 tx=entity[i].tx-1; ty=entity[i].ty;
\r
348 if (entity[i].obsmode1 && (ObstructionAt(tx,ty) || AEntityObsAt(tx,ty)))
\r
349 { movesuccess=0; return; }
\r
351 if (entity[i].facing!=2)
\r
353 entity[i].delayct=0;
\r
354 entity[i].animofs=0;
\r
356 entity[i].x--; entity[i].facing=2; entity[i].moving=3; entity[i].ctr=15;
\r
357 entity[i].tx--; movesuccess=1; entity[i].reset=0;
\r
364 tx=entity[i].tx; ty=entity[i].ty-1;
\r
365 if (entity[i].obsmode1 && (ObstructionAt(tx,ty) || AEntityObsAt(tx,ty)))
\r
366 { movesuccess=0; return; }
\r
367 if (entity[i].facing!=1)
\r
369 entity[i].delayct=0;
\r
370 entity[i].animofs=0;
\r
372 entity[i].y--; entity[i].facing=1; entity[i].moving=2; entity[i].ctr=15;
\r
373 entity[i].ty--; movesuccess=1; entity[i].reset=0;
\r
376 void MoveDown(int i)
\r
380 tx=entity[i].tx; ty=entity[i].ty+1;
\r
381 if (entity[i].obsmode1 && (ObstructionAt(tx,ty) || AEntityObsAt(tx,ty)))
\r
382 { movesuccess=0; return; }
\r
383 if (entity[i].facing!=0)
\r
385 entity[i].delayct=0;
\r
386 entity[i].animofs=0;
\r
388 entity[i].y++; entity[i].facing=0; entity[i].moving=1; entity[i].ctr=15;
\r
389 entity[i].ty++; movesuccess=1; entity[i].reset=0;
\r
392 void Wander1(int i)
\r
394 if (!entity[i].data1)
\r
396 entity[i].data2=rnd(0,3);
\r
397 entity[i].data1=entity[i].step+1;
\r
399 if (entity[i].data1==1)
\r
401 entity[i].delayctr++;
\r
402 if (entity[i].delayctr>=entity[i].delay)
\r
406 if (entity[i].data1>1)
\r
408 switch(entity[i].data2)
\r
410 case 0: MoveUp(i); break;
\r
411 case 1: MoveDown(i); break;
\r
412 case 2: MoveLeft(i); break;
\r
413 case 3: MoveRight(i); break;
\r
416 if (entity[i].data1==1)
\r
418 entity[i].delayctr=0;
\r
419 entity[i].animofs=0;
\r
420 entity[i].delayct=0;
\r
425 void Wander2(int i)
\r
427 if (!entity[i].data1)
\r
429 entity[i].data3=rnd(0,3);
\r
430 entity[i].data1=entity[i].step+1;
\r
432 if (entity[i].data1==1)
\r
434 entity[i].delayctr++;
\r
435 if (entity[i].delayctr>=entity[i].delay)
\r
439 if (entity[i].data1>1)
\r
441 switch(entity[i].data3)
\r
443 case 0: if (Zone(entity[i].tx,entity[i].ty-1)==entity[i].data2) MoveUp(i); break;
\r
444 case 1: if (Zone(entity[i].tx,entity[i].ty+1)==entity[i].data2) MoveDown(i); break;
\r
445 case 2: if (Zone(entity[i].tx-1,entity[i].ty)==entity[i].data2) MoveLeft(i); break;
\r
446 case 3: if (Zone(entity[i].tx+1,entity[i].ty)==entity[i].data2) MoveRight(i); break;
\r
449 if (entity[i].data1==1) entity[i].delayctr=0;
\r
453 void Wander3(int i)
\r
455 if (!entity[i].data1)
\r
457 entity[i].data2=rnd(0,3);
\r
458 entity[i].data1=entity[i].step+1;
\r
460 if (entity[i].data1==1)
\r
462 entity[i].delayctr++;
\r
463 if (entity[i].delayctr>=entity[i].delay)
\r
467 if (entity[i].data1>1)
\r
469 switch(entity[i].data2)
\r
471 case 0: if (entity[i].ty>entity[i].data3) MoveUp(i); break;
\r
472 case 1: if (entity[i].ty<entity[i].data6) MoveDown(i); break;
\r
473 case 2: if (entity[i].tx>entity[i].data2) MoveLeft(i); break;
\r
474 case 3: if (entity[i].tx<entity[i].data5) MoveRight(i); break;
\r
477 if (entity[i].data1==1) entity[i].delayct=0;
\r
481 void Whitespace(int i)
\r
483 while (*entity[i].scriptofs==' ')
\r
484 entity[i].scriptofs++;
\r
487 void GetArgMS(int i)
\r
493 while (*entity[i].scriptofs>=48 && *entity[i].scriptofs<=57)
\r
495 token[j]=*entity[i].scriptofs;
\r
496 entity[i].scriptofs++;
\r
500 entity[i].data1=atoi(token);
\r
503 void GetNextCommandMS(int i)
\r
507 s=*entity[i].scriptofs;
\r
508 entity[i].scriptofs++;
\r
511 case 'U': entity[i].mode=1; GetArgMS(i); break;
\r
512 case 'D': entity[i].mode=2; GetArgMS(i); break;
\r
513 case 'L': entity[i].mode=3; GetArgMS(i); break;
\r
514 case 'R': entity[i].mode=4; GetArgMS(i); break;
\r
515 case 'S': entity[i].mode=5; GetArgMS(i); break;
\r
516 case 'W': entity[i].mode=6; GetArgMS(i); entity[i].animofs=0;
\r
517 entity[i].delayct=0; break;
\r
518 case 0: switch (entity[i].facing)
\r
520 case 0: entity[i].animofs=0; entity[i].frame=chr[entity[i].chrindex].didle; break;
\r
521 case 1: entity[i].animofs=0; entity[i].frame=chr[entity[i].chrindex].uidle; break;
\r
522 case 2: entity[i].animofs=0; entity[i].frame=chr[entity[i].chrindex].lidle; break;
\r
523 case 3: entity[i].animofs=0; entity[i].frame=chr[entity[i].chrindex].ridle; break;
\r
525 entity[i].movecode=0; entity[i].mode=7; entity[i].data1=0;
\r
526 entity[i].scriptofs=0; entity[i].delayct=0; break;
\r
527 case 'C': entity[i].mode=8; GetArgMS(i); break;
\r
528 case 'B': entity[i].mode=9; break;
\r
529 case 'X': entity[i].mode=10; GetArgMS(i); break;
\r
530 case 'Y': entity[i].mode=11; GetArgMS(i); break;
\r
531 case 'F': entity[i].mode=12; GetArgMS(i); break;
\r
532 case 'Z': entity[i].mode=13; GetArgMS(i); break;
\r
533 default: err("Invalid entity movement script.");
\r
537 void MoveScript(int i)
\r
539 if (!entity[i].scriptofs) entity[i].scriptofs=(char *) (int) ms+(int) msbuf[entity[i].movescript];
\r
540 if (!entity[i].mode) GetNextCommandMS(i);
\r
542 switch(entity[i].mode)
\r
544 case 1: MoveUp(i); if (movesuccess) entity[i].data1--; break;
\r
545 case 2: MoveDown(i); if (movesuccess) entity[i].data1--; break;
\r
546 case 3: MoveLeft(i); if (movesuccess) entity[i].data1--; break;
\r
547 case 4: MoveRight(i); if (movesuccess) entity[i].data1--; break;
\r
548 case 5: entity[i].speed=entity[i].data1; entity[i].data1=0; break;
\r
549 case 6: entity[i].data1--; break;
\r
551 case 8: ExecuteEvent(entity[i].data1); entity[i].data1=0; break;
\r
552 case 9: entity[i].scriptofs=(char *) (int) ms+(int) msbuf[entity[i].movescript];
\r
553 entity[i].data1=0; break;
\r
554 case 10: if (entity[i].tx<entity[i].data1) MoveRight(i);
\r
555 if (entity[i].tx>entity[i].data1) MoveLeft(i);
\r
556 if (entity[i].tx==entity[i].data1) entity[i].data1=0; break;
\r
558 case 11: if (entity[i].ty<entity[i].data1) MoveDown(i);
\r
559 if (entity[i].ty>entity[i].data1) MoveUp(i);
\r
560 if (entity[i].ty==entity[i].data1) entity[i].data1=0; break;
\r
562 case 12: entity[i].facing=entity[i].data1;
\r
565 switch(entity[i].facing)
\r
567 case 0: entity[i].frame=chr[entity[i].chrindex].didle; break;
\r
568 case 1: entity[i].frame=chr[entity[i].chrindex].uidle; break;
\r
569 case 2: entity[i].frame=chr[entity[i].chrindex].lidle; break;
\r
570 case 3: entity[i].frame=chr[entity[i].chrindex].ridle; break;
\r
574 case 13: entity[i].specframe=entity[i].data1;
\r
575 entity[i].data1=0; break;
\r
577 if (!entity[i].data1) entity[i].mode=0;
\r
580 void TestActive(int i)
\r
584 dx=abs(entity[i].x - player->x);
\r
585 dy=abs(entity[i].y - player->y);
\r
586 if ((dx<=16 && dy<=3) || (dx<=3 && dy<=16))
\r
588 if (!entity[i].expand4 && !invc)
\r
590 entity[i].expand4=1;
\r
591 ExecuteEvent(entity[i].actscript);
\r
595 entity[i].expand4=0;
\r
598 void ProcessEntity1(int i)
\r
600 entity[i].speedct=0;
\r
601 if (entity[i].actm) TestActive(i);
\r
603 if (!entity[i].moving)
\r
605 switch(entity[i].movecode)
\r
608 case 1: Wander1(i); break;
\r
609 case 2: Wander2(i); break;
\r
610 case 3: Wander3(i); break;
\r
611 case 4: MoveScript(i); break;
\r
612 default: err("unknown entity movement pattern.");
\r
614 if (!entity[i].reset) return;
\r
617 if (entity[i].reset)
\r
619 entity[i].animofs=0;
\r
620 entity[i].delayct=0;
\r
624 if (entity[i].moving)
\r
626 if (entity[i].moving==1)
\r
627 { entity[i].y++; entity[i].ctr--; AnimateEntity(&entity[i]); }
\r
628 if (entity[i].moving==2)
\r
629 { entity[i].y--; entity[i].ctr--; AnimateEntity(&entity[i]); }
\r
630 if (entity[i].moving==4)
\r
631 { entity[i].x++; entity[i].ctr--; AnimateEntity(&entity[i]); }
\r
632 if (entity[i].moving==3)
\r
633 { entity[i].x--; entity[i].ctr--; AnimateEntity(&entity[i]); }
\r
634 if (!entity[i].ctr) { entity[i].reset=1; entity[i].moving=0; }
\r
638 void ProcessEntity(int i)
\r
641 if (player==&entity[i]) return;
\r
642 if (entity[i].speed<4)
\r
644 switch (entity[i].speed)
\r
646 case 1: if (entity[i].speedct<3) { entity[i].speedct++; return; }
\r
647 case 2: if (entity[i].speedct<2) { entity[i].speedct++; return; }
\r
648 case 3: if (entity[i].speedct<1) { entity[i].speedct++; return; }
\r
651 if (entity[i].speed<5)
\r
654 entity[i].speedct=0;
\r
656 switch (entity[i].speed)
\r
658 case 5: for (j=0; j<2; j++) { ProcessEntity1(i); } return;
\r
659 case 6: for (j=0; j<3; j++) { ProcessEntity1(i); } return;
\r
660 case 7: for (j=0; j<4; j++) { ProcessEntity1(i); } return;
\r
664 void ProcessEntities()
\r
669 for (i=0; i<entities; i++) ProcessEntity(i);
\r
675 sprintf(strbuf,"There are
\82%d~ entities on this map.",entities);
\r
676 Con_Printf(strbuf);
\r
677 sprintf(strbuf,"
\82%d~ of those are onscreen / active.",cc);
\r
678 Con_Printf(strbuf);
\r
681 void ListActiveEnts()
\r
685 Con_Printf("Active entities:");
\r
686 for (i=0; i<cc; i++)
\r
688 sprintf(strbuf,"
\82%d~",entidx[i]);
\r
689 Con_Printf(strbuf);
\r
696 i=atoi((char *) args[1]);
\r
699 sprintf(strbuf,"Desc:
\82%s~", entity[i].desc); Con_Printf(strbuf);
\r
700 sprintf(strbuf,"tx:
\82%d~ ty:
\82%d~", entity[i].tx, entity[i].ty); Con_Printf(strbuf);
\r
701 sprintf(strbuf,"CHR index: %d", entity[i].chrindex); Con_Printf(strbuf);
\r
704 int AllocateEntity(int x1, int y1, char *fname)
\r
706 entity[entities].chrindex=CacheCHR(fname);
\r
707 entity[entities].x=x1*16;
\r
708 entity[entities].y=y1*16;
\r
709 entity[entities].tx=x1;
\r
710 entity[entities].ty=y1;
\r
711 entity[entities].speed=4;
\r
712 entity[entities].obsmode1=1;
\r
713 entity[entities].obsmode2=1;
\r
715 return (entities-1);
\r