//:://///////////////////////////////////////////// //:: Combat Dummy //:: Copyright (c) 2001 Bioware Corp. //::////////////////////////////////////////////// /* usage: assign this script to the OnDamaged event of a placeable make placeable non-static & usable assign 10.000 hp to the placeable set hardness and all saves to 0 */ //::////////////////////////////////////////////// //:: Created By: Knat //:: Modified by styzygy //::////////////////////////////////////////////// // ------------------------------------------------------------------- // --------------- COMBAT DUMMY START -------------------------------- // ------------------------------------------------------------------- /* combat dummy does not need a creature object to simulate and track combat status. player needs to deplete a set amount of HP, depending on their level, to complete a training session. rewards XP one time during a specific timespan (default 1 hour realtime) ONLY if they complete a training session. (no xp if they abort/cancel a session) players will be able to re-train asap, but will not get any XP until said timer runs out. it's not possible to "farm" xp, it's just a small spice up... you can change those values easily. combat dummy records some basic statistics like number of hits and total damage done. It displays them during combat and a final statistic after the session is complete. adds FX based on player damage accounts for multiplayer.. only one active training session per dummy. other PCs attacking the same dummy get ignored (in theory at least, never tested so far) */ int PLC_DUMMY_HP_MODIFIER = 10; // dummy-hp = modifier + (pc-level * modifier) float PLC_DUMMY_XP_MODIFIER = 0.0; // adjust this to control XP output (xp = dummy-hp * modifier) //float PLC_DUMMY_XP_TIMER = 3600.0; // XP timer in seconds. Player won't get XP again until this timer runs out void DummyRespawn(string sResRef, location lLoc) { CreateObject(OBJECT_TYPE_PLACEABLE, sResRef, lLoc); } void DummyCheckSession(object oDummy = OBJECT_SELF) { object oUser = GetLocalObject(oDummy,"PLC_INTERACT_USER"); if(GetIsObjectValid(oUser)) { if(GetIsInCombat(oUser)) // still in combat, restart timer DelayCommand(5.0,DummyCheckSession(oDummy)); else { // not in combat, abort session FloatingTextStringOnCreature("Session Aborted...",oUser); // delete & re-create dummy string sResRef = GetResRef(oDummy); location lLoc = GetLocation(oDummy); AssignCommand(GetModule(),DelayCommand(0.1,DummyRespawn(sResRef,lLoc)));; DestroyObject(oDummy); } } } void DummyMain(object oPC, object oDummy) { // initial user interaction, starts training session if( !GetLocalInt(oDummy,"PLC_INTERACT_INUSE") ) { //DelayCommand(5.0,CombatDummyReport(oPC, oDummy)); // set target PC SetLocalObject(oDummy,"PLC_INTERACT_USER",oPC); // set InUse flag SetLocalInt(oDummy,"PLC_INTERACT_INUSE",TRUE); // set strength of dummy based on player level SetLocalInt(oDummy,"PLC_INTERACT_SESSIONHP", (GetHitDice(oPC) * PLC_DUMMY_HP_MODIFIER ) + PLC_DUMMY_HP_MODIFIER ); // initiate "check for session abort/cancel" // this function polls every 5 sec and checks if the player is still // fighting with the dummy. if not, session gets aborted // and dummy gets re-created. this deletes all variables and resets // everything to clean state. // this "pseudo heartbeat" only runs during PC<>dummy interaction // and is very small DelayCommand(5.0,DummyCheckSession(oDummy)); // FloatingTextStringOnCreature("Session Started - Dummy Strength: "+IntToString(GetLocalInt(oDummy,"PLC_INTERACT_SESSIONHP")),oPC); // if(GetLocalInt(oPC,"PLC_INTERACT_COMBATDUMMY_NOXP")) // FloatingTextStringOnCreature("You won't get XP this time",oPC); } object oUser = GetLocalObject(oDummy,"PLC_INTERACT_USER"); if(oUser == oPC) { int nSessionHP = GetLocalInt(oDummy,"PLC_INTERACT_SESSIONHP"); int nTotalDamage = GetMaxHitPoints() - GetCurrentHitPoints(); int nHitCount; int nLastTotalDamage = GetLocalInt(oDummy,"PLC_INTERACT_LASTHP"); nHitCount = GetLocalInt(oDummy,"PLC_INTERACT_HITCOUNT"); // register hit if(nTotalDamage > nLastTotalDamage) { SetLocalInt(oDummy,"PLC_INTERACT_LASTHP", nTotalDamage); nHitCount++; SetLocalInt(oDummy,"PLC_INTERACT_HITCOUNT", nHitCount); } // apply some effects based on damage int nDamage = nTotalDamage - nLastTotalDamage; if (nDamage > 24) ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(75), OBJECT_SELF); if (nDamage > 12) ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(281), OBJECT_SELF); if (nDamage > 6) ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(112), OBJECT_SELF); if (nDamage > 3) ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(236 + d3()), OBJECT_SELF); SendMessageToPC(oPC, IntToString(nTotalDamage) + " damage, " + IntToString(nHitCount) + " hits."); if(nTotalDamage > nSessionHP) // session done - dummy HP depleted { if (GetLocalInt(OBJECT_SELF,"DO_ONCE") == 1) return; SetLocalInt(OBJECT_SELF,"DO_ONCE",1); FloatingTextStringOnCreature("You have finished your training session",oPC); // report final statistic int nAvgDam = nTotalDamage/nHitCount; AssignCommand(GetModule(),DelayCommand(2.0,FloatingTextStringOnCreature("You inflicted "+IntToString(nTotalDamage)+ " damage to the dummy with "+IntToString(nHitCount)+" hits",oPC))); AssignCommand(GetModule(),DelayCommand(4.0,FloatingTextStringOnCreature("Your average damage is " + IntToString(nAvgDam)+" per attack",oPC))); int nIdx; int nRank; int nHolder=0; int nAvgDamHolder; string sNameHolder; for (nIdx = 10; nIdx > 0; nIdx--) { nHolder = GetCampaignInt("PGC3_DATA", "BOBO_AVGDAM_"+IntToString(nIdx)); //debug SendMessageToPC(oPC, IntToString(nIdx)+"="+IntToString(nHolder)); if (nAvgDam > nHolder) nRank = nIdx; } //debug SendMessageToPC(oPC, "Rank="+IntToString(nRank)); if (nRank < 11) AssignCommand(GetModule(),DelayCommand(6.0,FloatingTextStringOnCreature("Your score ranks #"+IntToString(nRank)+" on the Best Bobo Bashers list.", oPC))); // save stats in campaign db for (nIdx = 9; nIdx >= nRank; nIdx--) { nAvgDamHolder = GetCampaignInt("PGC3_DATA", "BOBO_AVGDAM_"+IntToString(nIdx)); sNameHolder = GetCampaignString("PGC3_DATA", "BOBO_NAME_"+IntToString(nIdx)); SetCampaignInt("PGC3_DATA", "BOBO_AVGDAM_"+IntToString(nIdx+1), nAvgDamHolder); SetCampaignString("PGC3_DATA", "BOBO_NAME_"+IntToString(nIdx+1), sNameHolder); //debug SendMessageToPC(oPC, IntToString(nIdx+1)+") Set Name="+sNameHolder+", Set AvgDam="+IntToString(nAvgDamHolder)); } SetCampaignInt("PGC3_DATA", "BOBO_AVGDAM_"+IntToString(nRank), nAvgDam); //SetCampaignString("PGC3_DATA", "BOBO_NAME_"+IntToString(nRank), GetName(oPC)); string sWeaponName = "bare hands"; if (GetLastWeaponUsed(oPC) != OBJECT_INVALID) sWeaponName = GetName(GetLastWeaponUsed(oPC)); SetCampaignString("PGC3_DATA", "BOBO_NAME_"+IntToString(nRank), GetName(oPC) + ", with " + sWeaponName); // award xp only if permitted // if(GetLocalInt(oPC,"PLC_INTERACT_COMBATDUMMY_NOXP")) // AssignCommand(GetModule(),DelayCommand(8.0,FloatingTextStringOnCreature("No XP reward ! You trained too recently...",oPC))); // else // { // // award xp and report it to the user // AssignCommand(GetModule(),DelayCommand(8.0,FloatingTextStringOnCreature("You earned " + IntToString( FloatToInt( IntToFloat(nSessionHP) * PLC_DUMMY_XP_MODIFIER ) ) + " xp for your training",oPC))); // AssignCommand(GetModule(),DelayCommand(9.0,GiveXPToCreature(oPC,FloatToInt( IntToFloat(nSessionHP)* PLC_DUMMY_XP_MODIFIER ) ) ) ); // set no-xp flag // SetLocalInt(oPC,"PLC_INTERACT_COMBATDUMMY_NOXP",TRUE); // set deletion timer for no-xp flag // AssignCommand(GetModule(),DelayCommand(PLC_DUMMY_XP_TIMER,DeleteLocalInt(oPC,"PLC_INTERACT_COMBATDUMMY_NOXP"))); // } // destroy & re-create dummy string sResRef = GetResRef(oDummy); location lLoc = GetLocation(oDummy); AssignCommand(GetModule(),DelayCommand(0.1,DummyRespawn(sResRef,lLoc)));; DestroyObject(oDummy); } } else SendMessageToPC(oPC,"Bobo is busy now."); } void main() { object oLastDamager = GetLastDamager(); if(GetIsObjectValid(oLastDamager)) { //SendMessageToPC(GetFirstPC(),"Last Damager: "+GetName(oLastDamager)); DummyMain(oLastDamager,OBJECT_SELF); } }