static automap_path *automap_find_path(loc_node *location, loc_node *dest, BOOL by_walking) { automap_path *path = NULL; automap_path *rev; automap_path newnode; loc_node *p; /* Find the distances of all nodes from dest */ n_hash_enumerate(&rooms, make_distant); automap_calc_distances(dest, 0, by_walking); /* If dest isn't reachable, location's distance will still be infinite */ if(location->dist == INFINITY) return NULL; /* At each step, go toward a nearer node 'till we're there */ p = location; while(p != dest) { unsigned i; unsigned best_dir; glui32 best_dist = INFINITY; loc_node *best_node = NULL; unsigned maxdir = by_walking ? NUM_EXITS : NUM_DIRS; for(i = 0; i < maxdir; i++) { loc_node *thisdest; if(by_walking) thisdest = p->exits[i]; else thisdest = automap_edge_follow(p, i); if(thisdest && thisdest->dist < best_dist) { best_dir = i; best_dist = thisdest->dist; best_node = thisdest; } } if(!best_node) { n_show_error(E_SYSTEM, "couldn't find path there", 0); return NULL; } newnode.loc = p; newnode.dir = best_dir; LEadd(path, newnode); p = best_node; } rev = NULL; while(path) { LEadd(rev, *path); LEremove(path); } return rev; }
static void automap_find_cycles(loc_node *location, automap_path *curpath) { unsigned i; location->touched = TRUE; for(i = 0; i < NUM_DIRS; i++) { loc_node *thisdest = automap_edge_follow(location, i); if(thisdest && thisdest->found) { automap_path newnode; newnode.dir = i; newnode.loc = location; LEadd(curpath, newnode); if(thisdest->touched) { /* Found a cycle! */ int cyclelength = 0; automap_path *p; cycleequation *cycle = NULL; cycleequation newcycle; for(p = curpath; p; p=p->next) { int dir = p->dir; newcycle.var = &(p->loc->outgoing[dir]->guess_length); newcycle.min = &(p->loc->outgoing[dir]->min_length); newcycle.xcoefficient = dirways[dir].deltax; newcycle.ycoefficient = dirways[dir].deltay; LEadd(cycle, newcycle); cyclelength++; if(p->loc == thisdest) { /* Found the relevant endpoint */ if(cyclelength <= 2) /* Ignore two nodes going to each other */ LEdestroy(cycle); else automap_add_cycle(cycle); /* automap_add_cycle gets ownership */ break; } } if(!p) { /* The cycle had already been found */ LEdestroy(cycle); } } else { automap_find_cycles(thisdest, curpath); } LEremove(curpath); } } }
void kill_undo(void) { n_free(prevstate); prevstate = 0; while(movelist) { n_free(movelist->delta); n_free(movelist->stackchunk); LEremove(movelist); } move_index = 0; #ifdef DEBUGGING n_free(automap_undoslot.z_mem); n_free(automap_undoslot.stackchunk); automap_undoslot.z_mem = NULL; automap_undoslot.z_memsize = 0; automap_undoslot.PC = 0; automap_undoslot.stackchunk = NULL; automap_undoslot.stackchunksize = 0; automap_undoslot.stacklength = 0; #endif }
BOOL saveundo(BOOL in_instruction) { move_difference newdiff; strid_t stack; stream_result_t poo; if(!allow_saveundo) return TRUE; /* In games which provide @save_undo, we will have already issued a faked saveundo before the first @save_undo hits, since there hadn't been any @save_undo before the first read line. So when this happens, wipe the fake saveundo in favor of the real one */ if(in_instruction && movelist && !movelist->next && !movelist->PC_in_instruction) init_undo(); if(!quetzal_diff(z_memory, prevstate, dynamic_size, &newdiff.delta, &newdiff.deltalength, TRUE)) return FALSE; #ifdef PARANOID { char *newmem = (char *) n_malloc(dynamic_size); n_memcpy(newmem, prevstate, dynamic_size); quetzal_undiff(newmem, dynamic_size, newdiff.delta, newdiff.deltalength, TRUE); if(n_memcmp(z_memory, newmem, dynamic_size)) { n_show_error(E_SAVE, "save doesn't match itself", 0); } n_free(newmem); } #endif newdiff.PC = PC; newdiff.oldPC = oldPC; newdiff.PC_in_instruction = in_instruction; newdiff.stacklength = get_quetzal_stack_size(); newdiff.stackchunk = (zbyte *) n_malloc(newdiff.stacklength); stack = glk_stream_open_memory((char *) newdiff.stackchunk, newdiff.stacklength, filemode_Write, 0); if(!stack) { n_free(newdiff.delta); n_free(newdiff.stackchunk); return FALSE; } if(!quetzal_stack_save(stack)) { glk_stream_close(stack, NULL); n_free(newdiff.delta); n_free(newdiff.stackchunk); return FALSE; } glk_stream_close(stack, &poo); if(poo.writecount != newdiff.stacklength) { n_show_error(E_SAVE, "incorrect stack size assessment", poo.writecount); n_free(newdiff.delta); n_free(newdiff.stackchunk); return FALSE; } while(move_index-- > 0) { n_free(movelist->delta); n_free(movelist->stackchunk); LEremove(movelist); } LEadd(movelist, newdiff); move_index++; n_memcpy(prevstate, z_memory, dynamic_size); has_done_save_undo = TRUE; return TRUE; }