#include #include #include #include #include #include #include #include #include #include #include #include #include #include "engine1.h" #include #include "globals.h" #include "specproc.h" #include "temp_storage.h" #include #include TMULTI_ACTION_RECORD *macros; //tabulka make TMULTI_ACTION_STATE macro_state_block; static const TMULTI_ACTION *first_macro; void *macro_block = NULL; int macro_block_size = 0; int32_t sound_side_flags=0; //kopie flagu steny pro zvuk static char codelock_memory[16][8]; static short rand_value; static int program_counter=0; static char trig_group; SGlobalEventDef GlobEventList[MAGLOB_NEXTID]; void propadnout(int sector); #define TRIG_GROUP 1 #define TRIG_SECTOR 2 char get_player_triggered(int p) { return (trig_group & (1<=0) trig_group=(char)load; return trig_group; } static inline size_t read_4b(const char *c) { size_t b1 = c[0]; size_t b2 = c[1]; size_t b3 = c[2]; size_t b4 = c[3]; return b1 | (b2<<8) | (b3 << 16) | (b4 << 24); } static void read_macro_item(const char *iter, size_t sz, TMULTI_ACTION *target) { assert (sz <= sizeof(TMULTI_ACTION)); char action = ((const TMULTI_ACTION *)iter)->general.action; char fixpad = action == MA_FIREB || action == MA_LOADL || action == MA_DROPI || action == MA_CREAT || action == MA_LOCK || action == MA_CUNIQ || action == MA_GUNIQ || action == MA_IFJMP || action == MA_HAVIT || action == MA_RANDJ || action == MA_ISFLG || action == MA_PICKI || action == MA_SNDEX || action == MA_IFACT || action == MA_CALLS || action == MA_MOVEG || action == MA_CHFLG || action == MA_WBOOK || action == MA_GOMOB || action == MA_SHRMA || action == MA_MONEY || action == MA_PLAYA; if (fixpad) { char *src = (char *)(iter)+3; char *dst = (char *)(target)+4; memcpy(target, iter, 3); memcpy(dst, src, sz - 3); } else { memcpy(target, iter, sz); } } void load_macros(int size,void *data) { char *iter = data; size_t count_s = mapsize*4; size_t count_m = 0; size_t i; while ((i = read_4b(iter)) != 0) { iter += 4; while ((i = read_4b(iter)) != 0) { iter += 4 + i; ++count_m; } iter +=4; } //records[count_s] + states[count_m] + data[count_m] size_t total_size = sizeof(TMULTI_ACTION_RECORD) * count_s + count_m + sizeof(TMULTI_ACTION)*count_m; macro_block = getmem(total_size); memset(macro_block,0, total_size); macros = macro_block; macro_state_block.states = (uint8_t *)(macros+count_s); macro_state_block.count = count_m; TMULTI_ACTION *m_iter = (TMULTI_ACTION *)(macro_state_block.states + count_m); #ifndef NDEBUG TMULTI_ACTION *m_end = m_iter + count_m; #endif first_macro = m_iter; iter = data; while ((i = read_4b(iter)) != 0) { assert(m_iter < m_end); assert(i < (size_t)mapsize*4); TMULTI_ACTION_RECORD *r = macros+i; size_t count = 0; r->action_list = m_iter; iter += 4; i = read_4b(iter); iter += 4; if (i) { do { read_macro_item(iter, i, m_iter); ++m_iter; iter += i; i = read_4b(iter); iter += 4; ++count; } while (i != 0); } r->count = count; } assert((char *)m_iter == (char *)macro_block+total_size); macro_block_size = total_size; } void macro_disp_text(int text,char glob) { if (glob) bott_disp_text(texty[text]); else bott_disp_text(level_texts[text]); } void macro_fireball(const TMA_FIREBALL *z,int sector,int dir) { LETICI_VEC *fly; TITEM *it; fly=create_fly(); it=glob_items+z->item-1; fly->items=NULL; fly->item=z->item; fly->xpos=z->xpos; fly->ypos=z->ypos*128/500-64; fly->zpos=z->zpos*128/320; fly->speed=z->speed; fly->velocity=0; fly->flags=FLY_IN_FLY | (!it->hmotnost?FLY_NEHMOTNA:0) | (it->flags & ITF_DESTROY?FLY_DESTROY:0)|FLY_NEDULEZITA; fly->sector=sector; fly->smer=(dir+2)&3; fly->owner=0; fly->hit_bonus=0; fly->damage=0; fly->counter=0; if (fly->flags & FLY_DESTROY)fly->lives=it->user_value; add_fly(fly); } void macro_sound(const TMA_SOUND *p,int psect,int pdir,int sect,int dir) { char up=4; if (sound_side_flags & SD_PRIM_FORV) up=2; if (~(p->bit16) & up) { if (psect) { play_effekt(map_coord[sect].x,map_coord[sect].y,map_coord[psect].x,map_coord[psect].y,psect,pdir,p); } else { play_effekt(0,0,0,0,-1,-1,p); } } } void macro_send_act(const TMA_SEND_ACTION *p) { delay_action(p->s_action,p->sector,p->side,p->change_bits<<24,0,p->delay); } void macro_load_another_map(const TMA_LOADLEV *z) { // int i,j=0; if (battle) return; group_all(); /* for(i=0;i=string) { if (*ends!=*endm) break; ends--;endm--; } if (endssector==sector && d->side==dir) { if (p==NULL) d_action=d->next;else p->next=d->next; free(d); return; } p=d; d=d->next; } } char if_lock(int side,int key_id,int level,const TMA_LOCK *lk) { int c; level; if (picked_item==NULL) { call_macro(side,MC_LOCKINFO); return 1; } c=picked_item[0]-1; c=glob_items[c].keynum; if (c==-1 && level!=-1) { int i,j=0,min=-1; THUMAN *h=postavy; int thlev; char s[100]; for(i=0;iused && h->groupnum==cur_group && h->vlastnosti[VLS_OBRAT]>min) {min=h->vlastnosti[VLS_OBRAT];j=i;}; h=postavy+j; if (level==0) level=100; if (level>=min) if ((int)rnd(100)<=level-min) { sprintf(s,texty[158+h->female],h->jmeno); bott_disp_text(s); destroy_items(picked_item); free(picked_item); picked_item=NULL;pick_set_cursor(); return 1; } thlev=rnd(min); if (thlev>level) { sprintf(s,texty[154+h->female],h->jmeno); bott_disp_text(s); return 0; } sprintf(s,texty[156+h->female],h->jmeno); bott_disp_text(s); return 1; } if (c!=key_id || !c) { call_macro(side,MC_TOUCHFAIL); return 1; } return 0; } void xchg_block(void *b1, void *b2, int leng) { uint8_t *p1 = (uint8_t *)b1; uint8_t *p2 = (uint8_t *)b2; for (int i = 0; i < leng; i++) { uint8_t temp = p1[i]; p1[i] = p2[i]; p2[i] = temp; } } void propadnout(int sector) { short *i,c,m1,m2; for(c=0;c<4;c++) { pop_item(sector,c,0,&i); while(i!=NULL) { push_item(sector,c,i); pop_item(sector,c,0,&i); } } if (mob_map[sector]) { m1=mob_map[sector]-1; m2=mobs[m1].next-1; mob_map[sector]=0; if (map_sectors[sector].sector_type==S_DIRA) { mobs[m1].sector=map_sectors[sector].sector_tag; if (m2>=0) mobs[m2].sector=mobs[m1].sector; } mob_map[mobs[m1].sector]=m1+1; } postavy_propadnout(sector); } static void swap_sectors(const TMA_SWAPS *sws) { TSECTOR *ss1=&map_sectors[sws->sector1],*ss2=&map_sectors[sws->sector2]; TSTENA *sd1=&map_sides[sws->sector1<<2],*sd2=&map_sides[sws->sector2<<2]; char c=4; char st1=ss2->sector_type,st2=ss1->sector_type; for(c=0;c<4;c++) xchg_block(sd1+c,sd2+c,sizeof(TSTENA)); xchg_block(ss1,ss2,sizeof(TSECTOR)); if (st1==S_DIRA || st1==S_VODA) propadnout(sws->sector1); if (st2==S_DIRA || st2==S_VODA) propadnout(sws->sector2); recheck_button(sws->sector1,0); recheck_button(sws->sector2,0); } static void hit_1_player(int postava,const TMA_WOUND *w,int chaos) { int mode=w->pflags>>1; int zivel=mode-2; int dostal; THUMAN *h=postavy+postava; if (mode==0) { dostal=w->minor+rnd(w->major-w->minor+1); } else if (mode==1) { short vls[24]; memset(vls,0,sizeof(vls)); vls[VLS_UTOK_L]=w->minor; vls[VLS_UTOK_H]=w->major; dostal=vypocet_zasahu(vls,h->vlastnosti,chaos,0,0); } else { short vls[24]; memset(vls,0,sizeof(vls)); vls[VLS_MGSIL_L]=w->minor; vls[VLS_MGSIL_H]=w->major; vls[VLS_MGZIVEL]=zivel; dostal=vypocet_zasahu(vls,h->vlastnosti,chaos,0,0); } player_hit(h,dostal,0); } static void hit_player(const TMA_WOUND *w,int sector) { int i,pocet,r; for(i=0,pocet=0;ipflags & 1) { r=rnd(pocet)+1; for(i=0;i0;i++) if (get_player_triggered(i)) r--; i--; hit_1_player(i,w,pocet); } else for(i=0;i (size_t)abs_pos) { ret.action_list += abs_pos; ret.count -= abs_pos; } return ret; } static char monster_in_game(void) { int i; for(i=0;i=0) { m2=mobs[m1].next-1; if (~mobs[m1].vlajky & MOB_MOBILE && (m2<0 || ~mobs[m2].vlajky & MOB_MOBILE)) *monster_test=1; } return !*monster_test; } static char monster_in_room(int sector) { char monster_test=0; is_monster(sector, &monster_test); if (!monster_test) labyrinth_find_path(sector,65535,SD_MONST_IMPS,is_monster,NULL, &monster_test); return monster_test; } static int if_jump(const TMA_TWOP *i,int side,int abs_pos) { TSTENA *sd=map_sides+side; int go,test,flag; char ok=0; test=abs(i->parm1)-1; go=i->parm2; flag=sd->flags; if (test<32) ok=(flag & (1<>2);break; } if (i->parm1<0) ok=!ok; if (ok) return go+abs_pos;else return -1; } static int if_have_item(const TMA_TWOP *i,int abs_pos) { int go,test,ip; char ok=0; test=abs(i->parm1); go=i->parm2; for(ip=0;ipparm1<0) ok=!ok; if (ok) return go+abs_pos;else return -1; } static int ma_randjmp(const TMA_TWOP *i,int abs_pos) { int go,test; char ok=0; test=i->parm1; go=i->parm2; if (rand_value==-1) rand_value=rnd(100); ok=rand_valueparm1)-1; go=i->parm2; ok=(test==act); if (i->parm1<0) ok=!ok; if (ok) return go+abs_pos;else return -1; } static int ma_if_flag(const TMA_TWOP *i,int abs_pos) { int go,test; char ok=0; test=abs(i->parm1)-1; go=i->parm2; ok=test_flag(test); if (i->parm1<0) ok=!ok; if (ok) return go+abs_pos;else return -1; } static int ma_picki(const TMA_TWOP *i,int abs_pos) { int go,test; char ok=0; test=abs(i->parm1); go=i->parm2; if (picked_item!=NULL) ok=picked_item[0]==test;else ok=0; if (i->parm1<0) ok=!ok; if (ok) return go+abs_pos;else return -1; } static void ma_wbook(const TMA_LOADLEV *l) { const char *s = build_pathname(2, gpathtable[SR_MAP], l->name); add_text_to_book(s,l->start_pos); play_fx_at(FX_BOOK); } static void ma_send_experience(int32_t what) { int maxl,i; THUMAN *h; for(i=0,maxl=0,h=postavy;iused && maxllevel) maxl=h->level; for(i=0,h=postavy;iused) { h->exp+=what*h->level/maxl; check_player_new_level(h); } bott_draw(0); } static void ma_move_group(int where,int turn,char effect) { if (!save_load_trigger(-1)) return; if (!effect) { int i; THUMAN *h=postavy; for(i=0;isektor=where,h->direction=turn; viewsector=where; viewdir=turn; } else { THUMAN *h=postavy; int i=0; int sctr=0; char kdo=0; for(i=0;i=0) trig_group|=1<used && h->groupnum==cur_group) trig_group|=1<>=2; for(i=0,h=postavy;iused && h->sektor==side && h->inmaphash == current_map_hash) trig_group|=1<twop.parm2-q->twop.parm1+1); x+=q->twop.parm1; x=create_item_money(x)-1; if (x) macro_drop_item(sect,side,x); } void macro_change_music(int textindex) { char *trackdef=level_texts[textindex]; create_playlist(trackdef); change_music(get_next_music_from_playlist()); } void macro_register_global_event(const TMULTI_ACTION *q) { GlobEventList[q->globe.event].cancel=q->globe.cancel; GlobEventList[q->globe.event].sector=q->globe.sector; GlobEventList[q->globe.event].side=q->globe.side; GlobEventList[q->globe.event].param=q->globe.param; if (q->globe.event>=MAGLOB_ONTIMER1 && q->globe.event<=MAGLOB_ONTIMER4) { if (GlobEventList[q->globe.event].param>0) GlobEventList[q->globe.event].param+=game_time; else { int32_t den=24*60*6; int32_t cas=((-GlobEventList[q->globe.event].param/100)*60+(-GlobEventList[q->globe.event].param%100))*6; int32_t curtm=game_time % den; if (cas<=curtm) cas+=den; GlobEventList[q->globe.event].param=game_time-curtm+cas; } } } void call_macro_ex(int side,int flags, int runatside); void call_macro(int side,int flags) { call_macro_ex(side,flags,side); } void call_macro_ex(int side, int flags, int runatside) { const TMULTI_ACTION *z; short saved_trigger; short ls = last_send_action; short save_rand; if (side >= mapsize * 4) return; if (runatside >= mapsize * 4) return; save_rand = rand_value; rand_value = -1; TMULTI_ACTION_RECORD mrec = macros[runatside]; program_counter = 0; int count_actions = 0; if (mrec.action_list == NULL) return; SEND_LOG("(MULTIACTIONS) Start: Side %.1f Call %X",(float)(runatside/4)+((float)(runatside & 3)/10),flags); saved_trigger = save_load_trigger(-1); if (flags & (MC_PASSSUC | MC_PASSFAIL | MC_EXIT)) build_trig_group(TRIG_GROUP, 0); else build_trig_group(TRIG_SECTOR, side); while (mrec.count != 0) { z = mrec.action_list; if (z->general.flags & flags) { int jmp_to = -1; char cancel_enabled = 1; int stindex = z - first_macro; if (!z->general.once || !macro_state_block.states[stindex]) { macro_state_block.states[stindex] = 1; count_actions++; switch (z->general.action) { default: break; case MA_GEN: break; case MA_SOUND: macro_sound(&z->sound, side >> 2, side & 3, viewsector, viewdir); break; case MA_TEXTG: macro_disp_text(z->text.textindex, 1); break; case MA_TEXTL: macro_disp_text(z->text.textindex, 0); break; case MA_SENDA: macro_send_act(&z->send_a); break; case MA_FIREB: macro_fireball(&z->fireball, side >> 2, side & 3); break; case MA_DESTI: if (picked_item != NULL) { destroy_items(picked_item); free(picked_item); picked_item = NULL; pick_set_cursor(); } break; case MA_LOADL: macro_load_another_map(&z->loadlev); break; case MA_DROPI: macro_drop_item(side >> 2, side & 0x3, z->dropi.item); break; case MA_CREAT: macro_create_item(z->dropi.item); break; case MA_DIALG: start_dialog(z->text.textindex, -1); break; case MA_SSHOP: enter_shop(z->text.textindex); break; case MA_CLOCK: cancel_enabled = decode_lock(z->clock.znak, z->clock.string, z->clock.codenum); break; case MA_CACTN: cancel_action(z->cactn.sector, z->cactn.dir); break; case MA_LOCK: cancel_enabled = if_lock(side, z->lock.key_id, z->lock.thieflevel, &z->lock); break; case MA_SWAPS: swap_sectors(&z->swaps); break; case MA_WOUND: hit_player(&z->wound, side >> 2); break; case MA_IFJMP: jmp_to = if_jump(&z->twop, side, program_counter); break; case MA_STORY: write_story_text(level_texts[z->text.textindex]); break; case MA_HAVIT: jmp_to = if_have_item(&z->twop, program_counter); break; case MA_SNDEX: ma_send_experience(z->twop.parm1); break; case MA_IFACT: jmp_to = ma_test_action(&z->twop, ls, program_counter); break; case MA_CALLS: if (call_map_event(z->twop.parm1, side >> 2, side & 3, z->twop.parm2, flags)) call_macro(side, MC_SPEC_SUCC); break; case MA_MOVEG: ma_move_group(z->twop.parm1, z->twop.parm2 & 3, z->twop.parm2 >> 7); break; case MA_PLAYA: ma_play_anim(z->loadlev.name, z->loadlev.dir); break; case MA_ISFLG: jmp_to = ma_if_flag(&z->twop, program_counter); break; case MA_CHFLG: change_flag(z->twop.parm1, (char) z->twop.parm2); break; case MA_CUNIQ: macro_drop_item(side >> 2, side & 0x3, create_unique_item(&z->uniq.item) - 1); break; case MA_MONEY: ma_drop_money(side >> 2, side & 0x3, z); break; case MA_GUNIQ: macro_create_item(create_unique_item(&z->uniq.item) - 1); break; case MA_PICKI: jmp_to = ma_picki(&z->twop, program_counter); break; case MA_WBOOK: ma_wbook(&z->loadlev); break; case MA_RANDJ: jmp_to = ma_randjmp(&z->twop, program_counter); break; case MA_ENDGM: unwire_proc(); send_message(E_CLOSE_MAP, (void*) 255); break; case MA_GOMOB: ma_control_mob(z->twop.parm1, z->twop.parm2); break; case MA_SHRMA: call_macro_ex(side, flags, z->twop.parm1 * 4 + z->twop.parm2); break; case MA_MUSIC: macro_change_music(z->text.textindex); break; case MA_GLOBE: macro_register_global_event(z); break; } } if (jmp_to != -1) { mrec = go_macro(runatside, jmp_to); program_counter = jmp_to; } else if (z->general.cancel && cancel_enabled) { break; } else { program_counter++; ++mrec.action_list; --mrec.count; } } else { program_counter++; ++mrec.action_list; --mrec.count; } } rand_value = save_rand; save_load_trigger(saved_trigger); SEND_LOG("(MULTIACTIONS) End: Sector %d, Side %d, Call %X, Actions: %d",runatside/4,runatside & 3,flags,count_actions); } char save_codelocks(TMPFILE_WR *fsta) { temp_storage_write(codelock_memory,sizeof(codelock_memory)*1,fsta); return 0; } char load_codelocks(TMPFILE_RD *fsta) { return !temp_storage_read(codelock_memory,sizeof(codelock_memory)*1,fsta); }