/** * All but object "save_item" are removed from the container list. Note * we have to becareful to remove the inventories of objects in the * cauldron inventory (ex icecube has stuff in it). * @param first_ob Container from which to remove. * @param save_item What item not to remove. Can be NULL. */ static void remove_contents(object *first_ob, object *save_item) { object *next, *tmp = first_ob; while (tmp) { next = tmp->below; if (tmp == save_item) { if (!(tmp = next)) { break; } else { next = next->below; } } if (tmp->inv) { remove_contents(tmp->inv, NULL); } remove_ob(tmp); tmp = next; } }
/** * Essentially a wrapper for make_item_from_recipe() and * insert_ob_in_ob(). If the caster has some alchemy skill, then they * might gain some exp from (successful) fabrication of the product. * If nbatches==-1, don't give exp for this creation (random generation/ * failed recipe) * @param caster Who is trying to do alchemy. * @param cauldron Container used for alchemy. * @param rp Recipe attempted. * @param nbatches If -1, don't give exp for this creation (random * generation/failed recipe) * @return Generated item, can be NULL if contents were destroyed. */ static object *attempt_recipe(object *caster, object *cauldron, int ability, recipe *rp, int nbatches) { object *item = NULL; /* This should be passed to this function, not too effiecent cpu use this way */ int batches = abs(nbatches); /* Code required for this recipe, search the caster */ if (rp->keycode) { object *tmp; for (tmp = caster->inv; tmp != NULL; tmp = tmp->below) { if (tmp->type == FORCE && tmp->slaying && !strcmp(rp->keycode, tmp->slaying)) { break; } } /* Failure - no code found */ if (tmp == NULL) { new_draw_info(NDI_UNIQUE, caster, "You know the ingredients, but not the technique. Go learn how to do this recipe."); return NULL; } } #ifdef EXTREME_ALCHEMY_DEBUG LOG(llevDebug, "DEBUG: attempt_recipe(): got %d nbatches\n", nbatches); LOG(llevDebug, "DEBUG: attempt_recipe(): using recipe %s\n", rp->title ? rp->title : "unknown"); #endif if ((item = make_item_from_recipe(cauldron, rp)) != NULL) { remove_contents(cauldron->inv, item); /* adj lvl, nrof on caster level */ adjust_product(item, ability, rp->yield ? (rp->yield * batches) : batches); if (!item->env && (item = insert_ob_in_ob(item, cauldron)) == NULL) { new_draw_info(NDI_UNIQUE, caster, "Nothing happened."); } else { new_draw_info_format(NDI_UNIQUE, caster, "The %s %s.", cauldron->name, cauldron_sound()); } } return item; }
/** * Ouch. We didn't get the formula we wanted. This function simulates the * backfire effects -- worse effects as the level increases. If * SPELL_FAILURE_EFFECTS is defined some really evil things can happen to * the would be alchemist. This table probably needs some adjustments for * playbalance. * @param op Who tried to do alchemy. * @param cauldron Container that was used. * @param rp Rrecipe that failed. * @param danger Danger value, the higher the more evil the effect. */ static void alchemy_failure_effect(object *op, object *cauldron, recipe *rp, int danger) { int level = 0; if (!op || !cauldron) { return; } if (danger > 1) { level = rndm(1, danger); } #ifdef ALCHEMY_DEBUG LOG(llevDebug, "DEBUG: Alchemy_failure_effect(): using level=%d\n", level); #endif /* possible outcomes based on level */ /* Ingredients destroyed, and possibly create slag. */ if (level < 25) { object *item = NULL; /* Slag created */ if (rndm(0, 2)) { object *tmp = cauldron->inv; int weight = 0; uint16 material = M_STONE; /* Slag has coadded ingredient properties */ while (tmp) { weight += tmp->weight; if (!(material & tmp->material)) { material = material | tmp->material; } tmp = tmp->below; } tmp = get_archetype("rock"); tmp->weight = weight; tmp->value = 0; tmp->material = material; FREE_AND_COPY_HASH(tmp->name, "slag"); item = insert_ob_in_ob(tmp, cauldron); CLEAR_FLAG(tmp, FLAG_CAN_ROLL); CLEAR_FLAG(tmp, FLAG_NO_PICK); CLEAR_FLAG(tmp, FLAG_NO_PASS); } remove_contents(cauldron->inv, item); new_draw_info_format(NDI_UNIQUE, op, "The %s %s.", cauldron->name, cauldron_sound()); return; } /* Make tained item. */ else if (level < 40) { object *tmp = NULL; if (!rp) { if ((rp = get_random_recipe(NULL)) == NULL) { return; } } if ((tmp = attempt_recipe(op, cauldron, 1, rp, -1))) { /* Curse it */ if (!QUERY_FLAG(tmp, FLAG_CURSED)) { SET_FLAG(tmp, FLAG_CURSED); } /* Special stuff for consumables */ if (tmp->type == FOOD) { tmp->stats.sp = 0; /* Poisonous */ if (rndm(0, 1)) { tmp->type = FOOD; tmp->stats.hp = rndm(0, 149); } } /* Unsaleable item */ tmp->value = 0; /* Change stats downward */ do { change_attr_value(&tmp->stats, rndm(0, 6), (signed char) (-1 * (rndm(1, 3)))); } while (rndm(0, 2)); } return; } /* Make random recipe. */ if (level == 40) { recipelist *fl; int numb = numb_ob_inside(cauldron); /* Take a lower recipe list */ fl = get_formulalist(numb - 1); if (fl && (rp = get_random_recipe(fl))) { /* Even though random, don't grant user any EXP for it */ (void) attempt_recipe(op, cauldron, 1, rp, -1); } else { alchemy_failure_effect(op, cauldron, rp, level - 1); } return; } /* Infuriate NPCs */ else if (level < 45) { alchemy_failure_effect(op, cauldron, rp, level - 5); return; } /* Minor explosion/fireball */ else if (level < 50) { object *tmp; remove_contents(cauldron->inv, NULL); switch (rndm(0, 2)) { case 0: tmp = get_archetype("bomb"); tmp->stats.dam = rndm(1, level); tmp->stats.hp = rndm(1, level); new_draw_info_format(NDI_UNIQUE, op, "The %s creates a bomb!", cauldron->name); break; default: tmp = get_archetype("fireball"); tmp->stats.dam = rndm(1, level) / 5 + 1; tmp->stats.hp = rndm(1, level) / 10 + 2; new_draw_info_format(NDI_UNIQUE, op, "The %s erupts in flame!", cauldron->name); break; } tmp->x = cauldron->x, tmp->y = cauldron->y; insert_ob_in_map(tmp, op->map, NULL, 0); return; } /* Create monster */ else if (level < 60) { new_draw_info_format(NDI_UNIQUE, op, "The %s %s.", cauldron->name, cauldron_sound()); remove_contents(cauldron->inv, NULL); return; } /* Major fire */ else if (level < 80) { remove_contents(cauldron->inv, NULL); #if 0 fire_arch_from_position(cauldron, cauldron, cauldron->x, cauldron->y, 0, spellarch[SP_L_FIREBALL], SP_L_FIREBALL, NULL); #endif new_draw_info_format(NDI_UNIQUE, op, "The %s erupts in flame!", cauldron->name); return; } /* Whammy the cauldron */ else if (level < 100) { if (!QUERY_FLAG(cauldron, FLAG_CURSED)) { SET_FLAG(cauldron, FLAG_CURSED); } else { cauldron->magic--; } cauldron->magic -= rndm(0, 4); if (rndm(0, 1)) { remove_contents(cauldron->inv, NULL); new_draw_info_format(NDI_UNIQUE, op, "Your %s turns darker then makes a gulping sound!", cauldron->name); } else { new_draw_info_format(NDI_UNIQUE, op, "Your %s becomes darker.", cauldron->name); } return; } /* Summon evil monsters */ else if (level < 110) { object *tmp = get_random_mon(); remove_contents(cauldron->inv, NULL); if (!tmp) { alchemy_failure_effect(op, cauldron, rp, level); } return; } /* Combo effect */ else if (level < 150) { int roll = rndm(1, 3); while (roll) { alchemy_failure_effect(op, cauldron, rp, level - 39); roll--; } return; } /* Create random artifact */ else if (level == 151) { object *tmp; /* this is meant to be better than prior possiblity, * in this one, we allow *any* valid alchemy artifact * to be made (rather than only those on the given * formulalist) */ if (!rp) { rp = get_random_recipe((recipelist *) NULL); } if (rp && (tmp = get_archetype(rp->arch_name))) { generate_artifact(tmp, rndm(1, op->level / 2 + 1) + 1, 0, 99); if ((tmp = insert_ob_in_ob(tmp, cauldron))) { remove_contents(cauldron->inv, tmp); new_draw_info_format(NDI_UNIQUE, op, "The %s %s.", cauldron->name, cauldron_sound()); } } return; } /* Mana storm -- watch out! */ else if (level < 200) { new_draw_info(NDI_UNIQUE, op, "You unwisely release potent forces!"); remove_contents(cauldron->inv, NULL); cast_magic_storm(op, get_archetype("loose_magic"), level); return; } }
void Project::save( bool onlyProjectFile ) { bool anythingModified = FALSE; // save sources and forms if ( !onlyProjectFile ) { saveConnections(); for ( SourceFile *sf = sourcefiles.first(); sf; sf = sourcefiles.next() ) { anythingModified = anythingModified || sf->isModified(); if ( !sf->save() ) return; } for ( FormFile *ff = formfiles.first(); ff; ff = formfiles.next() ) { anythingModified = anythingModified || ff->isModified(); if ( !ff->save() ) return; } } if ( isDummy() || filename.isEmpty() ) return; if ( !modified ) { if ( singleProjectMode() ) { LanguageInterface *iface = MetaDataBase::languageInterface( language() ); if ( iface && iface->supports( LanguageInterface::CompressProject ) ) iface->compressProject( makeAbsolute( filename ), singleProFileName, anythingModified ); } return; } QFile f( filename ); QString original = ""; // read the existing file bool hasPreviousContents = FALSE; if ( f.open( IO_ReadOnly ) ) { QTextStream ts( &f ); original = ts.read(); f.close(); hasPreviousContents = TRUE; remove_contents( original, "{SOURCES+=" ); // ### compatibility with early 3.0 betas remove_contents( original, "DBFILE" ); remove_contents( original, "LANGUAGE" ); remove_contents( original, "TEMPLATE" ); removePlatformSettings( original, "CONFIG" ); removePlatformSettings( original, "DEFINES" ); removePlatformSettings( original, "LIBS" ); removePlatformSettings( original, "INCLUDEPATH" ); removePlatformSettings( original, "SOURCES" ); removePlatformSettings( original, "HEADERS" ); remove_multiline_contents( original, "FORMS" ); remove_multiline_contents( original, "INTERFACES" ); // compatibility remove_multiline_contents( original, "IMAGES" ); for ( QStringList::Iterator it = csList.begin(); it != csList.end(); ++it ) remove_contents( original, *it ); } if (!original.isEmpty()) { // Removes any new lines at the beginning of the file while (original.startsWith("\n")) original.remove(0, 1); } // the contents of the saved file QString contents; // template contents += "TEMPLATE\t= " + templ + "\n"; // language contents += "LANGUAGE\t= " + lang + "\n"; contents += "\n"; // config writePlatformSettings( contents, "CONFIG", cfg ); LanguageInterface *iface = MetaDataBase::languageInterface( lang ); if ( iface ) { QStringList sourceKeys; iface->sourceProjectKeys( sourceKeys ); for ( QStringList::Iterator spit = sourceKeys.begin(); spit != sourceKeys.end(); ++spit ) remove_multiline_contents( contents, *spit ); } // libs, defines, includes writePlatformSettings( contents, "LIBS", lbs ); writePlatformSettings( contents, "DEFINES", defs ); writePlatformSettings( contents, "INCLUDEPATH", inclPath ); writePlatformSettings( contents, "SOURCES", sources ); writePlatformSettings( contents, "HEADERS", headers ); // unix if ( !hasPreviousContents ) { contents += "unix|os2 {\n" " UI_DIR = .ui\n" " MOC_DIR = .moc\n" " OBJECTS_DIR = .obj\n" "}\n\n"; } // sources if ( !sourcefiles.isEmpty() && iface ) { QMap<QString, QStringList> sourceToKey; for ( SourceFile *f = sourcefiles.first(); f; f = sourcefiles.next() ) { QString key = iface->projectKeyForExtension( QFileInfo( f->fileName() ).extension() ); QStringList lst = sourceToKey[ key ]; lst << makeRelative( f->fileName() ); sourceToKey.replace( key, lst ); } for ( QMap<QString, QStringList>::Iterator skit = sourceToKey.begin(); skit != sourceToKey.end(); ++skit ) { QString part = skit.key() + "\t+= "; QStringList lst = *skit; for ( QStringList::Iterator sit = lst.begin(); sit != lst.end(); ++sit ) { part += *sit; part += ++sit != lst.end() ? " \\\n\t" : ""; --sit; } part += "\n\n"; contents += part; } } // forms and interfaces if ( !formfiles.isEmpty() ) { contents += "FORMS\t= "; for ( QPtrListIterator<FormFile> fit = formfiles; fit.current(); ++fit ) { contents += fit.current()->fileName() + (fit != formfiles.last() ? " \\\n\t" : ""); } contents += "\n\n"; } // images if ( !pixCollection->isEmpty() ) { contents += "IMAGES\t= "; QValueList<PixmapCollection::Pixmap> pixmaps = pixCollection->pixmaps(); for ( QValueList<PixmapCollection::Pixmap>::Iterator it = pixmaps.begin(); it != pixmaps.end(); ++it ) { contents += makeRelative( (*it).absname ); contents += ++it != pixmaps.end() ? " \\\n\t" : ""; --it; } contents += "\n\n"; } // database if ( !dbFile.isEmpty() ) contents += "DBFILE\t= " + dbFile + "\n"; // custom settings for ( QStringList::Iterator it = csList.begin(); it != csList.end(); ++it ) { QString val = *customSettings.find( *it ); if ( !val.isEmpty() ) contents += *it + "\t= " + val + "\n"; } if ( !f.open( IO_WriteOnly | IO_Translate ) ) { QMessageBox::warning( messageBoxParent(), "Save Project Failed", "Couldn't write project file " + filename ); return; } QTextStream os( &f ); os << contents; if (hasPreviousContents) os << original; f.close(); setModified( FALSE ); if ( singleProjectMode() ) { LanguageInterface *iface = MetaDataBase::languageInterface( language() ); if ( iface && iface->supports( LanguageInterface::CompressProject ) ) iface->compressProject( makeAbsolute( filename ), singleProFileName, TRUE ); } }